Custom notifications now working for emails, popups, blinks and sound.
Blinks might need some changes because, there's no need to formar or gather extra info, so, it might not be needed to allow plugin developers to register custom handlers for it.
This commit is contained in:
parent
1b7a50f88b
commit
8420d6105b
|
@ -36,38 +36,190 @@
|
|||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
from twisted.internet import defer
|
||||
from deluge import component
|
||||
from deluge.event import known_events
|
||||
from deluge.log import LOG as log
|
||||
|
||||
def get_resource(filename):
|
||||
import pkg_resources, os
|
||||
return pkg_resources.resource_filename("notifications",
|
||||
os.path.join("data", filename))
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
# Core -------------------------------------------------------------------------
|
||||
"smtp_enabled": False,
|
||||
"smtp_host": "",
|
||||
"smtp_port": 25,
|
||||
"smtp_user": "",
|
||||
"smtp_pass": "",
|
||||
"smtp_from": "",
|
||||
"smtp_tls": False, # SSL or TLS
|
||||
"smtp_recipients": [],
|
||||
# GTK UI -----------------------------------------------------------------------
|
||||
# BLINK
|
||||
"blink_enabled": False,
|
||||
# FLASH
|
||||
"flash_enabled": False,
|
||||
# POPUP
|
||||
"popup_enabled": False,
|
||||
# SOUND
|
||||
"sound_enabled": False,
|
||||
"sound_path": "",
|
||||
# Subscriptions ----------------------------------------------------------------
|
||||
"subscriptions": {
|
||||
"popup": [],
|
||||
"blink": [],
|
||||
"sound": [],
|
||||
"email": [],
|
||||
}
|
||||
|
||||
}
|
||||
class CustomNotifications(component.Component):
|
||||
def __init__(self, impl):
|
||||
component.Component.__init__(self, "Notifications")
|
||||
self.__impl = impl
|
||||
self.custom_notifications = {
|
||||
"email": {},
|
||||
"popup": {},
|
||||
"blink": {},
|
||||
"sound": {}
|
||||
}
|
||||
|
||||
def enable(self):
|
||||
pass
|
||||
|
||||
def disable(self):
|
||||
for kind in self.custom_notifications.iterkeys():
|
||||
for eventtype in self.custom_notifications[kind].copy().iterkeys():
|
||||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
self.__deregister_custom_provider(kind, eventtype)
|
||||
|
||||
|
||||
def handle_custom_email_notification(self, result):
|
||||
raise NotImplementedError("%s does not implement this function" %
|
||||
self.__class__.__name__)
|
||||
|
||||
def handle_custom_popup_notification(self, result):
|
||||
raise NotImplementedError("%s does not implement this function" %
|
||||
self.__class__.__name__)
|
||||
|
||||
def handle_custom_blink_notification(self, result):
|
||||
raise NotImplementedError("%s does not implement this function" %
|
||||
self.__class__.__name__)
|
||||
|
||||
def handle_custom_sound_notification(self, result):
|
||||
raise NotImplementedError("%s does not implement this function" %
|
||||
self.__class__.__name__)
|
||||
|
||||
def register_custom_email_notification(self, eventtype, handler):
|
||||
"""This is used to register email notifications for custom event types.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
Your handler should return a tuple of (email_subject, email_contents).
|
||||
"""
|
||||
if self.__impl != 'core':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"CorePlugin")
|
||||
self.__register_custom_provider('email', eventtype, handler)
|
||||
|
||||
def deregister_custom_email_notification(self, eventtype):
|
||||
if self.__impl != 'core':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"CorePlugin")
|
||||
self.__deregister_custom_provider('email', eventtype)
|
||||
|
||||
def register_custom_popup_notification(self, eventtype, handler):
|
||||
"""This is used to register popup notifications for custom event types.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
Your handler should return a tuple of (popup_title, popup_contents).
|
||||
"""
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__register_custom_provider('popup', eventtype, handler)
|
||||
|
||||
def deregister_custom_popup_notification(self, eventtype):
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__deregister_custom_provider('popup', eventtype)
|
||||
|
||||
def register_custom_blink_notification(self, eventtype, handler):
|
||||
"""This is used to register blink notifications for custom event types.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
Your handler should return `True` or `False` to blink or not the
|
||||
trayicon.
|
||||
"""
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__register_custom_provider('blink', eventtype, handler)
|
||||
|
||||
def deregister_custom_blink_notification(self, eventtype):
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__deregister_custom_provider('blink', eventtype)
|
||||
|
||||
def register_custom_sound_notification(self, eventtype, handler):
|
||||
"""This is used to register sound notifications for custom event types.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
Your handler should return either '' to use the sound defined on the
|
||||
notification preferences, the path to a sound file, which will then be
|
||||
played or None, where no sound will be played at all.
|
||||
"""
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__register_custom_provider('sound', eventtype, handler)
|
||||
|
||||
def deregister_custom_sound_notification(self, eventtype):
|
||||
if self.__impl != 'gtk':
|
||||
return defer.fail("This function can only be called from a "
|
||||
"GtkPlugin")
|
||||
self.__deregister_custom_provider('sound', eventtype)
|
||||
|
||||
def __handle_custom_providers(self, kind, eventtype, *args, **kwargs):
|
||||
log.debug("\n\nCalling CORE's custom email providers for %s: %s %s",
|
||||
eventtype, args, kwargs)
|
||||
if eventtype in self.config["subscriptions"][kind]:
|
||||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
log.debug("Found handler for kind %s: %s", kind, handler)
|
||||
custom_notif_func = getattr(self,
|
||||
'handle_custom_%s_notification' % kind)
|
||||
d = defer.maybeDeferred(handler, *args, **kwargs)
|
||||
d.addCallback(custom_notif_func)
|
||||
d.addCallback(self._on_notify_sucess, kind)
|
||||
d.addErrback(self._on_notify_failure, kind)
|
||||
return d
|
||||
|
||||
def __register_custom_provider(self, kind, eventtype, handler):
|
||||
if not self.__handled_eventtype(eventtype):
|
||||
return defer.succeed("Event not handled")
|
||||
if eventtype not in self.custom_notifications:
|
||||
def wrapper(*args, **kwargs):
|
||||
return self.__handle_custom_providers(kind, eventtype,
|
||||
*args, **kwargs)
|
||||
self.custom_notifications[kind][eventtype] = (wrapper, handler)
|
||||
else:
|
||||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
try:
|
||||
component.get("EventManager").register_event_handler(
|
||||
eventtype, wrapper
|
||||
)
|
||||
except KeyError:
|
||||
from deluge.ui.client import client
|
||||
client.register_event_handler(eventtype, wrapper)
|
||||
|
||||
def __deregister_custom_provider(self, kind, eventtype):
|
||||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
try:
|
||||
component.get("EventManager").deregister_event_handler(
|
||||
eventtype, wrapper
|
||||
)
|
||||
except KeyError:
|
||||
from deluge.ui.client import client
|
||||
client.deregister_event_handler(eventtype, wrapper)
|
||||
self.custom_notifications[kind].pop(eventtype)
|
||||
|
||||
def __handled_eventtype(self, eventtype):
|
||||
if eventtype not in known_events:
|
||||
log.error("The event \"%s\" is not known" % eventtype)
|
||||
return False
|
||||
if known_events[eventtype].__module__.startswith('deluge.event'):
|
||||
log.error("You cannot register custom notification providers "
|
||||
"for built-in event types.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _on_notify_sucess(self, result, kind):
|
||||
log.debug("\n\nNotification success using %s: %s", kind, result)
|
||||
return result
|
||||
|
||||
def _on_notify_failure(self, failure, kind):
|
||||
log.debug("\n\nNotification failure using %s: %s", kind, failure)
|
||||
return failure
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
import smtplib
|
||||
from twisted.internet import defer, threads
|
||||
from deluge.event import known_events, DelugeEvent
|
||||
from deluge.event import known_events
|
||||
from deluge.log import LOG as log
|
||||
from deluge.plugins.pluginbase import CorePluginBase
|
||||
import deluge.component as component
|
||||
|
@ -47,6 +47,7 @@ import deluge.configmanager
|
|||
from deluge.core.rpcserver import export
|
||||
|
||||
from test import TestEmailNotifications
|
||||
from common import CustomNotifications
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
"smtp_enabled": False,
|
||||
|
@ -63,12 +64,11 @@ DEFAULT_PREFS = {
|
|||
}
|
||||
}
|
||||
|
||||
class Core(CorePluginBase, component.Component):
|
||||
class Core(CorePluginBase, CustomNotifications):
|
||||
def __init__(self, plugin_name):
|
||||
CorePluginBase.__init__(self, plugin_name)
|
||||
component.Component.__init__(self, "Notifications")
|
||||
self.email_message_providers = {}
|
||||
self.tn = TestEmailNotifications()
|
||||
CustomNotifications.__init__(self, 'core')
|
||||
self.tn = TestEmailNotifications('core')
|
||||
|
||||
def enable(self):
|
||||
self.config = deluge.configmanager.ConfigManager(
|
||||
|
@ -78,15 +78,11 @@ class Core(CorePluginBase, component.Component):
|
|||
)
|
||||
log.debug("\n\nENABLING CORE NOTIFICATIONS\n\n")
|
||||
self.tn.enable()
|
||||
# import sys
|
||||
# print '\n\n', [(n, k.__module__) for n, k in known_events.items()]
|
||||
# print [f for f in sys.modules.keys() if f.startswith("deluge.event")]
|
||||
|
||||
def disable(self):
|
||||
self.tn.disable()
|
||||
log.debug("\n\nDISABLING CORE NOTIFICATIONS\n\n")
|
||||
for eventtype in self.email_message_providers.keys():
|
||||
self.deregister_email_message_provider(eventtype)
|
||||
CustomNotifications.disable(self)
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
@ -116,55 +112,7 @@ class Core(CorePluginBase, component.Component):
|
|||
log.debug("Handled Notification Events: %s", handled_events)
|
||||
return handled_events
|
||||
|
||||
def register_email_message_provider(self, eventtype, handler):
|
||||
"""This is used to register email formatters for custom event types.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
You're handler should return a tuple of (subject, email_contents).
|
||||
"""
|
||||
if eventtype not in known_events:
|
||||
raise Exception("The event \"%s\" is not known" % eventtype)
|
||||
if known_events[eventtype].__module__.startswith('deluge.event'):
|
||||
raise Exception("You cannot register email message providers for "
|
||||
"built-in event types.")
|
||||
if eventtype not in self.email_message_providers:
|
||||
def wrapper(*args, **kwargs):
|
||||
return self._handle_custom_email_message_providers(eventtype,
|
||||
*args,
|
||||
**kwargs)
|
||||
self.email_message_providers[eventtype] = (wrapper, handler)
|
||||
else:
|
||||
wrapper, handler = self.email_message_providers[eventtype]
|
||||
component.get("EventManager").register_event_handler(
|
||||
eventtype, wrapper
|
||||
)
|
||||
|
||||
def deregister_email_message_provider(self, eventtype):
|
||||
wrapper, handler = self.email_message_providers[eventtype]
|
||||
component.get("EventManager").deregister_event_handler(
|
||||
eventtype, wrapper
|
||||
)
|
||||
self.email_message_providers.pop(eventtype)
|
||||
|
||||
def _handle_custom_email_message_providers(self, event, *args, **kwargs):
|
||||
if not self.config['smtp_enabled']:
|
||||
return defer.succeed("SMTP notification not enabled.")
|
||||
|
||||
log.debug("\n\nCalling CORE's custom email providers for %s: %s %s",
|
||||
event, args, kwargs)
|
||||
if event in self.config["subscriptions"]["email"]:
|
||||
wrapper, handler = self.email_message_providers[event]
|
||||
log.debug("Found handler: %s", handler)
|
||||
d = defer.maybeDeferred(handler, *args, **kwargs)
|
||||
d.addCallback(self._prepare_email)
|
||||
d.addCallback(self._on_notify_sucess)
|
||||
d.addErrback(self._on_notify_failure)
|
||||
return d
|
||||
|
||||
|
||||
def _prepare_email(self, result):
|
||||
def handle_custom_email_notification(self, result):
|
||||
if not self.config['smtp_enabled']:
|
||||
return defer.succeed("SMTP notification not enabled.")
|
||||
subject, message = result
|
||||
|
@ -262,11 +210,11 @@ Subject: %(subject)s
|
|||
return d
|
||||
|
||||
|
||||
def _on_notify_sucess(self, result):
|
||||
log.debug("\n\nEMAIL Notification success: %s", result)
|
||||
return result
|
||||
|
||||
|
||||
def _on_notify_failure(self, failure):
|
||||
log.debug("\n\nEMAIL Notification failure: %s", failure)
|
||||
return failure
|
||||
# def _on_notify_sucess(self, result):
|
||||
# log.debug("\n\nEMAIL Notification success: %s", result)
|
||||
# return result
|
||||
#
|
||||
#
|
||||
# def _on_notify_failure(self, failure):
|
||||
# log.debug("\n\nEMAIL Notification failure: %s", failure)
|
||||
# return failure
|
||||
|
|
|
@ -61,7 +61,8 @@ except ImportError:
|
|||
POPUP_AVAILABLE = False
|
||||
|
||||
# Relative imports
|
||||
from common import get_resource
|
||||
from common import get_resource, CustomNotifications
|
||||
from test import TestEmailNotifications
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
# BLINK
|
||||
|
@ -86,10 +87,11 @@ RECIPIENT_FIELD, RECIPIENT_EDIT = range(2)
|
|||
SUB_NOT_SOUND) = range(6)
|
||||
|
||||
|
||||
class GtkUI(GtkPluginBase, component.Component):
|
||||
class GtkUI(GtkPluginBase, CustomNotifications):
|
||||
def __init__(self, plugin_name):
|
||||
GtkPluginBase.__init__(self, plugin_name)
|
||||
component.Component.__init__(self, "Notifications")
|
||||
CustomNotifications.__init__(self, 'gtk')
|
||||
self.tn = TestEmailNotifications('gtk')
|
||||
|
||||
def enable(self):
|
||||
self.config = deluge.configmanager.ConfigManager(
|
||||
|
@ -227,7 +229,11 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
client.register_event_handler("TorrentFinishedEvent",
|
||||
self._on_torrent_finished_event)
|
||||
|
||||
self.tn.enable()
|
||||
|
||||
def disable(self):
|
||||
self.tn.disable()
|
||||
CustomNotifications.disable(self)
|
||||
component.get("Preferences").remove_page("Notifications")
|
||||
component.get("PluginManager").deregister_hook("on_apply_prefs",
|
||||
self.on_apply_prefs)
|
||||
|
@ -266,24 +272,6 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
current_blink_subscriptions.append(event)
|
||||
if sound:
|
||||
current_sound_subscriptions.append(event)
|
||||
# saved_popup_subscriptions = self.config['subscriptions']['popup']
|
||||
# saved_blink_subscriptions = self.config['subscriptions']['blink']
|
||||
# saved_sound_subscriptions = self.config['subscriptions']['sound']
|
||||
# for event in current_popup_subscriptions:
|
||||
# saved_popup_subscriptions.remove(event)
|
||||
# for event in saved_blink_subscriptions:
|
||||
# # De register
|
||||
# pass
|
||||
# for event in current_blink_subscriptions:
|
||||
# saved_blink_subscriptions.remove(event)
|
||||
# for event in saved_blink_subscriptions:
|
||||
# # De register
|
||||
# pass
|
||||
# for event in current_sound_subscriptions:
|
||||
# saved_sound_subscriptions.remove(event)
|
||||
# for event in saved_sound_subscriptions:
|
||||
# # De register
|
||||
# pass
|
||||
|
||||
self.config.config.update({
|
||||
"popup_enabled": self.glade.get_widget("popup_enabled").get_active(),
|
||||
|
@ -410,14 +398,14 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
else:
|
||||
self.glade.get_widget('sound_path').set_property('sensitive', False)
|
||||
|
||||
# Notification methods
|
||||
def blink(self):
|
||||
|
||||
def __blink(self):
|
||||
d = defer.maybeDeferred(self.systray.blink, True)
|
||||
d.addCallback(self._on_notify_sucess, "blink")
|
||||
d.addCallback(self._on_notify_failure, "blink")
|
||||
return d
|
||||
|
||||
def popup(self, title='', message=''):
|
||||
def __popup(self, title='', message=''):
|
||||
if not self.config['popup_enabled']:
|
||||
return defer.succeed(_("Popup notification is not enabled."))
|
||||
if not POPUP_AVAILABLE:
|
||||
|
@ -434,7 +422,7 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
return defer.fail(err_msg)
|
||||
return defer.succeed(_("Notification popup shown"))
|
||||
|
||||
def play_sound(self, sound_path=''):
|
||||
def __play_sound(self, sound_path=''):
|
||||
if not self.config['sound_enabled']:
|
||||
return defer.succeed(_("Sound notification not enabled"))
|
||||
if not SOUND_AVAILABLE:
|
||||
|
@ -490,14 +478,6 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
d.addErrback(self._on_notify_failure, 'popup')
|
||||
return d
|
||||
|
||||
def _on_notify_sucess(self, result, kind):
|
||||
log.debug("\n\nNotification success using %s: %s", kind, result)
|
||||
return result
|
||||
|
||||
def _on_notify_failure(self, failure, kind):
|
||||
log.debug("\n\nNotification failure using %s: %s", kind, failure)
|
||||
return failure
|
||||
|
||||
def _on_email_col_toggled(self, cell, path):
|
||||
self.subscriptions_model[path][SUB_NOT_EMAIL] = \
|
||||
not self.subscriptions_model[path][SUB_NOT_EMAIL]
|
||||
|
@ -518,3 +498,19 @@ class GtkUI(GtkPluginBase, component.Component):
|
|||
not self.subscriptions_model[path][SUB_NOT_SOUND]
|
||||
return
|
||||
|
||||
def handle_custom_popup_notification(self, result):
|
||||
title, message = result
|
||||
return defer.maybeDeferred(self.__popup, title, message)
|
||||
|
||||
def handle_custom_blink_notification(self, result):
|
||||
if result:
|
||||
return defer.maybeDeferred(self.__blink)
|
||||
return defer.succeed("Won't blink. The returned value from the custom "
|
||||
"handler was: %s", result)
|
||||
|
||||
def handle_custom_sound_notification(self, result):
|
||||
if isinstance(result, basestring):
|
||||
return defer.maybeDeferred(self.__play_sound, result)
|
||||
return defer.succeed("Won't play sound. The returned value from the "
|
||||
"custom handler was: %s", result)
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
|
@ -37,7 +37,7 @@
|
|||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
__plugin_name__ = "Notifications"
|
||||
__author__ = "Pedro Algarvio"
|
||||
|
@ -59,7 +59,7 @@ setup(
|
|||
license=__license__,
|
||||
long_description=__long_description__ if __long_description__ else __description__,
|
||||
|
||||
packages=[__plugin_name__.lower()],
|
||||
packages=find_packages(exclude=['**/test.py']),
|
||||
package_data = __pkg_data__,
|
||||
|
||||
entry_points="""
|
||||
|
|
Loading…
Reference in New Issue