Update the Notifications and FreeSpace plugins descriptions.
On the Notifications plugin, moved the Core and GtkUi notification implementations to `core.py` and `gtkui.py` respectively.
This commit is contained in:
parent
127b577440
commit
7812f7b4e4
|
@ -77,7 +77,7 @@ class Core(CorePluginBase):
|
||||||
self._timer = task.LoopingCall(self.update)
|
self._timer = task.LoopingCall(self.update)
|
||||||
else:
|
else:
|
||||||
self._timer.stop()
|
self._timer.stop()
|
||||||
self._interval = 15 #60
|
self._interval = 60 * 5 # every 5 minutes
|
||||||
if self.config['enabled']:
|
if self.config['enabled']:
|
||||||
self._timer.start(self._interval, False)
|
self._timer.start(self._interval, False)
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor
|
# 51 Franklin Street, Fifth Floor
|
||||||
# Boston, MA 02110-1301, USA.
|
# Boston, MA 02110-1301, USA.
|
||||||
#
|
#
|
||||||
# In addition, as a special exception, the copyright holders give
|
# In addition, as a special exception, the copyright holders give
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
# permission to link the code of portions of this program with the OpenSSL
|
||||||
|
@ -45,8 +45,8 @@ __author_email__ = "ufs@ufsoft.org"
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
__url__ = "http://deluge.ufsoft.org/hg/Notification/"
|
__url__ = "http://deluge.ufsoft.org/hg/Notification/"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
__description__ = ""
|
__description__ = "Plugin which continuously checks for available free space."
|
||||||
__long_description__ = """"""
|
__long_description__ = __description__
|
||||||
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
|
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
@ -67,7 +67,5 @@ setup(
|
||||||
%s = %s:CorePlugin
|
%s = %s:CorePlugin
|
||||||
[deluge.plugin.gtkui]
|
[deluge.plugin.gtkui]
|
||||||
%s = %s:GtkUIPlugin
|
%s = %s:GtkUIPlugin
|
||||||
[deluge.plugin.webui]
|
""" % ((__plugin_name__, __plugin_name__.lower())*2)
|
||||||
%s = %s:WebUIPlugin
|
|
||||||
""" % ((__plugin_name__, __plugin_name__.lower())*3)
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -37,12 +37,9 @@
|
||||||
# statement from all source files in the program, then also delete it here.
|
# statement from all source files in the program, then also delete it here.
|
||||||
#
|
#
|
||||||
|
|
||||||
import smtplib
|
from twisted.internet import defer
|
||||||
from twisted.internet import defer, threads
|
|
||||||
from deluge import component
|
from deluge import component
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
from deluge.ui.client import client
|
|
||||||
import deluge.common
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from deluge.event import known_events
|
from deluge.event import known_events
|
||||||
|
@ -50,20 +47,6 @@ except ImportError:
|
||||||
# Old deluge version
|
# Old deluge version
|
||||||
known_events = {}
|
known_events = {}
|
||||||
|
|
||||||
try:
|
|
||||||
import pygame
|
|
||||||
SOUND_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
SOUND_AVAILABLE = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pynotify
|
|
||||||
POPUP_AVAILABLE = True
|
|
||||||
if deluge.common.windows_check():
|
|
||||||
POPUP_AVAILABLE = False
|
|
||||||
except ImportError:
|
|
||||||
POPUP_AVAILABLE = False
|
|
||||||
|
|
||||||
|
|
||||||
def get_resource(filename):
|
def get_resource(filename):
|
||||||
import pkg_resources, os
|
import pkg_resources, os
|
||||||
|
@ -154,304 +137,3 @@ class CustomNotifications(object):
|
||||||
log.debug("Notification failure using %s: %s", kind, failure)
|
log.debug("Notification failure using %s: %s", kind, failure)
|
||||||
return failure
|
return failure
|
||||||
|
|
||||||
|
|
||||||
class CoreNotifications(CustomNotifications):
|
|
||||||
|
|
||||||
def enable(self):
|
|
||||||
CustomNotifications.enable(self)
|
|
||||||
self.register_custom_email_notification('TorrentFinishedEvent',
|
|
||||||
self._on_torrent_finished_event)
|
|
||||||
|
|
||||||
def disable(self):
|
|
||||||
self.deregister_custom_email_notification('TorrentFinishedEvent')
|
|
||||||
CustomNotifications.disable(self)
|
|
||||||
|
|
||||||
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).
|
|
||||||
"""
|
|
||||||
self._register_custom_provider('email', eventtype, handler)
|
|
||||||
|
|
||||||
def deregister_custom_email_notification(self, eventtype):
|
|
||||||
self._deregister_custom_provider('email', eventtype)
|
|
||||||
|
|
||||||
def handle_custom_email_notification(self, result, eventtype):
|
|
||||||
if not self.config['smtp_enabled']:
|
|
||||||
return defer.succeed("SMTP notification not enabled.")
|
|
||||||
subject, message = result
|
|
||||||
log.debug("Spawning new thread to send email with subject: %s: %s",
|
|
||||||
subject, message)
|
|
||||||
# Spawn thread because we don't want Deluge to lock up while we send the
|
|
||||||
# email.
|
|
||||||
return threads.deferToThread(self._notify_email, subject, message)
|
|
||||||
|
|
||||||
def get_handled_events(self):
|
|
||||||
handled_events = []
|
|
||||||
for evt in sorted(known_events.keys()):
|
|
||||||
if known_events[evt].__module__.startswith('deluge.event'):
|
|
||||||
if evt not in ('TorrentFinishedEvent',):
|
|
||||||
# Skip all un-handled built-in events
|
|
||||||
continue
|
|
||||||
classdoc = known_events[evt].__doc__.strip()
|
|
||||||
handled_events.append((evt, classdoc))
|
|
||||||
log.debug("Handled Notification Events: %s", handled_events)
|
|
||||||
return handled_events
|
|
||||||
|
|
||||||
def _notify_email(self, subject='', message=''):
|
|
||||||
log.debug("Email prepared")
|
|
||||||
to_addrs = '; '.join(self.config['smtp_recipients'])
|
|
||||||
headers = """\
|
|
||||||
From: %(smtp_from)s
|
|
||||||
To: %(smtp_recipients)s
|
|
||||||
Subject: %(subject)s
|
|
||||||
|
|
||||||
|
|
||||||
""" % {'smtp_from': self.config['smtp_from'],
|
|
||||||
'subject': subject,
|
|
||||||
'smtp_recipients': to_addrs}
|
|
||||||
|
|
||||||
message = '\r\n'.join((headers + message).splitlines())
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
# Python 2.6
|
|
||||||
server = smtplib.SMTP(self.config["smtp_host"],
|
|
||||||
self.config["smtp_port"],
|
|
||||||
timeout=60)
|
|
||||||
except:
|
|
||||||
# Python 2.5
|
|
||||||
server = smtplib.SMTP(self.config["smtp_host"],
|
|
||||||
self.config["smtp_port"])
|
|
||||||
except Exception, err:
|
|
||||||
err_msg = _("There was an error sending the notification email:"
|
|
||||||
" %s") % err
|
|
||||||
log.error(err_msg)
|
|
||||||
return err
|
|
||||||
|
|
||||||
security_enabled = self.config['smtp_tls']
|
|
||||||
|
|
||||||
if security_enabled:
|
|
||||||
server.ehlo()
|
|
||||||
if not server.esmtp_features.has_key('starttls'):
|
|
||||||
log.warning("TLS/SSL enabled but server does not support it")
|
|
||||||
else:
|
|
||||||
server.starttls()
|
|
||||||
server.ehlo()
|
|
||||||
|
|
||||||
if self.config['smtp_user'] and self.config['smtp_pass']:
|
|
||||||
try:
|
|
||||||
server.login(self.config['smtp_user'], self.config['smtp_pass'])
|
|
||||||
except smtplib.SMTPHeloError, err:
|
|
||||||
err_msg = _("The server didn't reply properly to the helo "
|
|
||||||
"greeting: %s") % err
|
|
||||||
log.error(err_msg)
|
|
||||||
return err
|
|
||||||
except smtplib.SMTPAuthenticationError, err:
|
|
||||||
err_msg = _("The server didn't accept the username/password "
|
|
||||||
"combination: %s") % err
|
|
||||||
log.error(err_msg)
|
|
||||||
return err
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
server.sendmail(self.config['smtp_from'], to_addrs, message)
|
|
||||||
except smtplib.SMTPException, err:
|
|
||||||
err_msg = _("There was an error sending the notification email:"
|
|
||||||
" %s") % err
|
|
||||||
log.error(err_msg)
|
|
||||||
return err
|
|
||||||
finally:
|
|
||||||
if security_enabled:
|
|
||||||
# avoid false failure detection when the server closes
|
|
||||||
# the SMTP connection with TLS enabled
|
|
||||||
import socket
|
|
||||||
try:
|
|
||||||
server.quit()
|
|
||||||
except socket.sslerror:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
server.quit()
|
|
||||||
return _("Notification email sent.")
|
|
||||||
|
|
||||||
|
|
||||||
def _on_torrent_finished_event(self, torrent_id):
|
|
||||||
log.debug("Handler for TorrentFinishedEvent called for CORE")
|
|
||||||
torrent = component.get("TorrentManager")[torrent_id]
|
|
||||||
torrent_status = torrent.get_status({})
|
|
||||||
# Email
|
|
||||||
subject = _("Finished Torrent \"%(name)s\"") % torrent_status
|
|
||||||
message = _(
|
|
||||||
"This email is to inform you that Deluge has finished "
|
|
||||||
"downloading \"%(name)s\", which includes %(num_files)i files."
|
|
||||||
"\nTo stop receiving these alerts, simply turn off email "
|
|
||||||
"notification in Deluge's preferences.\n\n"
|
|
||||||
"Thank you,\nDeluge."
|
|
||||||
) % torrent_status
|
|
||||||
return subject, message
|
|
||||||
|
|
||||||
d = defer.maybeDeferred(self.handle_custom_email_notification,
|
|
||||||
[subject, message],
|
|
||||||
"TorrentFinishedEvent")
|
|
||||||
d.addCallback(self._on_notify_sucess, 'email')
|
|
||||||
d.addErrback(self._on_notify_failure, 'email')
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
class GtkUiNotifications(CustomNotifications):
|
|
||||||
|
|
||||||
def enable(self):
|
|
||||||
CustomNotifications.enable(self)
|
|
||||||
self.register_custom_blink_notification(
|
|
||||||
"TorrentFinishedEvent", self._on_torrent_finished_event_blink
|
|
||||||
)
|
|
||||||
self.register_custom_sound_notification(
|
|
||||||
"TorrentFinishedEvent", self._on_torrent_finished_event_sound
|
|
||||||
)
|
|
||||||
self.register_custom_popup_notification(
|
|
||||||
"TorrentFinishedEvent", self._on_torrent_finished_event_popup
|
|
||||||
)
|
|
||||||
|
|
||||||
def disable(self):
|
|
||||||
self.deregister_custom_blink_notification("TorrentFinishedEvent")
|
|
||||||
self.deregister_custom_sound_notification("TorrentFinishedEvent")
|
|
||||||
self.deregister_custom_popup_notification("TorrentFinishedEvent")
|
|
||||||
CustomNotifications.disable(self)
|
|
||||||
|
|
||||||
def register_custom_popup_notification(self, eventtype, handler):
|
|
||||||
"""This is used to register popup notifications for custom event types.
|
|
||||||
|
|
||||||
:param event: the event name
|
|
||||||
:param type: string
|
|
||||||
:param handler: function, to be called when `:param:event` is emitted
|
|
||||||
|
|
||||||
Your handler should return a tuple of (popup_title, popup_contents).
|
|
||||||
"""
|
|
||||||
self._register_custom_provider('popup', eventtype, handler)
|
|
||||||
|
|
||||||
def deregister_custom_popup_notification(self, eventtype):
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
self._register_custom_provider('blink', eventtype, handler)
|
|
||||||
|
|
||||||
def deregister_custom_blink_notification(self, eventtype):
|
|
||||||
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: the event name
|
|
||||||
:type event: string
|
|
||||||
: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.
|
|
||||||
"""
|
|
||||||
self._register_custom_provider('sound', eventtype, handler)
|
|
||||||
|
|
||||||
def deregister_custom_sound_notification(self, eventtype):
|
|
||||||
self._deregister_custom_provider('sound', eventtype)
|
|
||||||
|
|
||||||
def handle_custom_popup_notification(self, result, eventtype):
|
|
||||||
title, message = result
|
|
||||||
return defer.maybeDeferred(self.__popup, title, message)
|
|
||||||
|
|
||||||
def handle_custom_blink_notification(self, result, eventtype):
|
|
||||||
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, eventtype):
|
|
||||||
if isinstance(result, basestring):
|
|
||||||
if not result and eventtype in self.config['custom_sounds']:
|
|
||||||
return defer.maybeDeferred(
|
|
||||||
self.__play_sound, self.config['custom_sounds'][eventtype])
|
|
||||||
return defer.maybeDeferred(self.__play_sound, result)
|
|
||||||
return defer.succeed("Won't play sound. The returned value from the "
|
|
||||||
"custom handler was: %s", result)
|
|
||||||
|
|
||||||
def __blink(self):
|
|
||||||
self.systray.blink(True)
|
|
||||||
return defer.succeed(_("Notification Blink shown"))
|
|
||||||
|
|
||||||
def __popup(self, title='', message=''):
|
|
||||||
import gtk
|
|
||||||
if not self.config['popup_enabled']:
|
|
||||||
return defer.succeed(_("Popup notification is not enabled."))
|
|
||||||
if not POPUP_AVAILABLE:
|
|
||||||
return defer.fail(_("pynotify is not installed"))
|
|
||||||
|
|
||||||
if pynotify.init("Deluge"):
|
|
||||||
icon = gtk.gdk.pixbuf_new_from_file_at_size(
|
|
||||||
deluge.common.get_pixmap("deluge.svg"), 48, 48)
|
|
||||||
self.note = pynotify.Notification(title, message)
|
|
||||||
self.note.set_icon_from_pixbuf(icon)
|
|
||||||
if not self.note.show():
|
|
||||||
err_msg = _("pynotify failed to show notification")
|
|
||||||
log.warning(err_msg)
|
|
||||||
return defer.fail(err_msg)
|
|
||||||
return defer.succeed(_("Notification popup shown"))
|
|
||||||
|
|
||||||
def __play_sound(self, sound_path=''):
|
|
||||||
if not self.config['sound_enabled']:
|
|
||||||
return defer.succeed(_("Sound notification not enabled"))
|
|
||||||
if not SOUND_AVAILABLE:
|
|
||||||
err_msg = _("pygame is not installed")
|
|
||||||
log.warning(err_msg)
|
|
||||||
return defer.fail(err_msg)
|
|
||||||
|
|
||||||
pygame.init()
|
|
||||||
try:
|
|
||||||
if not sound_path:
|
|
||||||
sound_path = self.config['sound_path']
|
|
||||||
alert_sound = pygame.mixer.music
|
|
||||||
alert_sound.load(sound_path)
|
|
||||||
alert_sound.play()
|
|
||||||
except pygame.error, message:
|
|
||||||
err_msg = _("Sound notification failed %s") % (message)
|
|
||||||
log.warning(err_msg)
|
|
||||||
return defer.fail(err_msg)
|
|
||||||
else:
|
|
||||||
msg = _("Sound notification Success")
|
|
||||||
log.info(msg)
|
|
||||||
return defer.succeed(msg)
|
|
||||||
|
|
||||||
def _on_torrent_finished_event_blink(self, torrent_id):
|
|
||||||
return True # Yes, Blink
|
|
||||||
|
|
||||||
def _on_torrent_finished_event_sound(self, torrent_id):
|
|
||||||
# Since there's no custom sound hardcoded, just return ''
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def _on_torrent_finished_event_popup(self, torrent_id):
|
|
||||||
d = client.core.get_torrent_status(torrent_id, ["name", "num_files"])
|
|
||||||
d.addCallback(self._on_torrent_finished_event_got_torrent_status)
|
|
||||||
d.addErrback(self._on_torrent_finished_event_torrent_status_failure)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def _on_torrent_finished_event_torrent_status_failure(self, failure):
|
|
||||||
log.debug("Failed to get torrent status to be able to show the popup")
|
|
||||||
|
|
||||||
def _on_torrent_finished_event_got_torrent_status(self, torrent_status):
|
|
||||||
log.debug("Handler for TorrentFinishedEvent GTKUI called. "
|
|
||||||
"Got Torrent Status")
|
|
||||||
title = _("Finished Torrent")
|
|
||||||
message = _("The torrent \"%(name)s\" including %(num_files)i "
|
|
||||||
"has finished downloading.") % torrent_status
|
|
||||||
return title, message
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,15 @@
|
||||||
# statement from all source files in the program, then also delete it here.
|
# statement from all source files in the program, then also delete it here.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
from twisted.internet import defer, threads
|
||||||
|
from deluge.event import known_events
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
from deluge.plugins.pluginbase import CorePluginBase
|
from deluge.plugins.pluginbase import CorePluginBase
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
from deluge.core.rpcserver import export
|
from deluge.core.rpcserver import export
|
||||||
|
|
||||||
from notifications.common import CoreNotifications
|
from notifications.common import CustomNotifications
|
||||||
|
|
||||||
DEFAULT_PREFS = {
|
DEFAULT_PREFS = {
|
||||||
"smtp_enabled": False,
|
"smtp_enabled": False,
|
||||||
|
@ -59,6 +62,152 @@ DEFAULT_PREFS = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CoreNotifications(CustomNotifications):
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
CustomNotifications.enable(self)
|
||||||
|
self.register_custom_email_notification('TorrentFinishedEvent',
|
||||||
|
self._on_torrent_finished_event)
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.deregister_custom_email_notification('TorrentFinishedEvent')
|
||||||
|
CustomNotifications.disable(self)
|
||||||
|
|
||||||
|
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).
|
||||||
|
"""
|
||||||
|
self._register_custom_provider('email', eventtype, handler)
|
||||||
|
|
||||||
|
def deregister_custom_email_notification(self, eventtype):
|
||||||
|
self._deregister_custom_provider('email', eventtype)
|
||||||
|
|
||||||
|
def handle_custom_email_notification(self, result, eventtype):
|
||||||
|
if not self.config['smtp_enabled']:
|
||||||
|
return defer.succeed("SMTP notification not enabled.")
|
||||||
|
subject, message = result
|
||||||
|
log.debug("Spawning new thread to send email with subject: %s: %s",
|
||||||
|
subject, message)
|
||||||
|
# Spawn thread because we don't want Deluge to lock up while we send the
|
||||||
|
# email.
|
||||||
|
return threads.deferToThread(self._notify_email, subject, message)
|
||||||
|
|
||||||
|
def get_handled_events(self):
|
||||||
|
handled_events = []
|
||||||
|
for evt in sorted(known_events.keys()):
|
||||||
|
if known_events[evt].__module__.startswith('deluge.event'):
|
||||||
|
if evt not in ('TorrentFinishedEvent',):
|
||||||
|
# Skip all un-handled built-in events
|
||||||
|
continue
|
||||||
|
classdoc = known_events[evt].__doc__.strip()
|
||||||
|
handled_events.append((evt, classdoc))
|
||||||
|
log.debug("Handled Notification Events: %s", handled_events)
|
||||||
|
return handled_events
|
||||||
|
|
||||||
|
def _notify_email(self, subject='', message=''):
|
||||||
|
log.debug("Email prepared")
|
||||||
|
to_addrs = '; '.join(self.config['smtp_recipients'])
|
||||||
|
headers = """\
|
||||||
|
From: %(smtp_from)s
|
||||||
|
To: %(smtp_recipients)s
|
||||||
|
Subject: %(subject)s
|
||||||
|
|
||||||
|
|
||||||
|
""" % {'smtp_from': self.config['smtp_from'],
|
||||||
|
'subject': subject,
|
||||||
|
'smtp_recipients': to_addrs}
|
||||||
|
|
||||||
|
message = '\r\n'.join((headers + message).splitlines())
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# Python 2.6
|
||||||
|
server = smtplib.SMTP(self.config["smtp_host"],
|
||||||
|
self.config["smtp_port"],
|
||||||
|
timeout=60)
|
||||||
|
except:
|
||||||
|
# Python 2.5
|
||||||
|
server = smtplib.SMTP(self.config["smtp_host"],
|
||||||
|
self.config["smtp_port"])
|
||||||
|
except Exception, err:
|
||||||
|
err_msg = _("There was an error sending the notification email:"
|
||||||
|
" %s") % err
|
||||||
|
log.error(err_msg)
|
||||||
|
return err
|
||||||
|
|
||||||
|
security_enabled = self.config['smtp_tls']
|
||||||
|
|
||||||
|
if security_enabled:
|
||||||
|
server.ehlo()
|
||||||
|
if not server.esmtp_features.has_key('starttls'):
|
||||||
|
log.warning("TLS/SSL enabled but server does not support it")
|
||||||
|
else:
|
||||||
|
server.starttls()
|
||||||
|
server.ehlo()
|
||||||
|
|
||||||
|
if self.config['smtp_user'] and self.config['smtp_pass']:
|
||||||
|
try:
|
||||||
|
server.login(self.config['smtp_user'], self.config['smtp_pass'])
|
||||||
|
except smtplib.SMTPHeloError, err:
|
||||||
|
err_msg = _("The server didn't reply properly to the helo "
|
||||||
|
"greeting: %s") % err
|
||||||
|
log.error(err_msg)
|
||||||
|
return err
|
||||||
|
except smtplib.SMTPAuthenticationError, err:
|
||||||
|
err_msg = _("The server didn't accept the username/password "
|
||||||
|
"combination: %s") % err
|
||||||
|
log.error(err_msg)
|
||||||
|
return err
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
server.sendmail(self.config['smtp_from'], to_addrs, message)
|
||||||
|
except smtplib.SMTPException, err:
|
||||||
|
err_msg = _("There was an error sending the notification email:"
|
||||||
|
" %s") % err
|
||||||
|
log.error(err_msg)
|
||||||
|
return err
|
||||||
|
finally:
|
||||||
|
if security_enabled:
|
||||||
|
# avoid false failure detection when the server closes
|
||||||
|
# the SMTP connection with TLS enabled
|
||||||
|
import socket
|
||||||
|
try:
|
||||||
|
server.quit()
|
||||||
|
except socket.sslerror:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
server.quit()
|
||||||
|
return _("Notification email sent.")
|
||||||
|
|
||||||
|
|
||||||
|
def _on_torrent_finished_event(self, torrent_id):
|
||||||
|
log.debug("Handler for TorrentFinishedEvent called for CORE")
|
||||||
|
torrent = component.get("TorrentManager")[torrent_id]
|
||||||
|
torrent_status = torrent.get_status({})
|
||||||
|
# Email
|
||||||
|
subject = _("Finished Torrent \"%(name)s\"") % torrent_status
|
||||||
|
message = _(
|
||||||
|
"This email is to inform you that Deluge has finished "
|
||||||
|
"downloading \"%(name)s\", which includes %(num_files)i files."
|
||||||
|
"\nTo stop receiving these alerts, simply turn off email "
|
||||||
|
"notification in Deluge's preferences.\n\n"
|
||||||
|
"Thank you,\nDeluge."
|
||||||
|
) % torrent_status
|
||||||
|
return subject, message
|
||||||
|
|
||||||
|
d = defer.maybeDeferred(self.handle_custom_email_notification,
|
||||||
|
[subject, message],
|
||||||
|
"TorrentFinishedEvent")
|
||||||
|
d.addCallback(self._on_notify_sucess, 'email')
|
||||||
|
d.addErrback(self._on_notify_failure, 'email')
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class Core(CorePluginBase, CoreNotifications):
|
class Core(CorePluginBase, CoreNotifications):
|
||||||
def __init__(self, plugin_name):
|
def __init__(self, plugin_name):
|
||||||
CorePluginBase.__init__(self, plugin_name)
|
CorePluginBase.__init__(self, plugin_name)
|
||||||
|
|
|
@ -49,8 +49,22 @@ import deluge.common
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
|
|
||||||
# Relative imports
|
# Relative imports
|
||||||
from notifications.common import (get_resource, GtkUiNotifications,
|
from common import get_resource, CustomNotifications
|
||||||
SOUND_AVAILABLE, POPUP_AVAILABLE)
|
|
||||||
|
try:
|
||||||
|
import pygame
|
||||||
|
SOUND_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
SOUND_AVAILABLE = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pynotify
|
||||||
|
POPUP_AVAILABLE = True
|
||||||
|
if deluge.common.windows_check():
|
||||||
|
POPUP_AVAILABLE = False
|
||||||
|
except ImportError:
|
||||||
|
POPUP_AVAILABLE = False
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PREFS = {
|
DEFAULT_PREFS = {
|
||||||
# BLINK
|
# BLINK
|
||||||
|
@ -76,6 +90,158 @@ RECIPIENT_FIELD, RECIPIENT_EDIT = range(2)
|
||||||
SUB_NOT_SOUND) = range(6)
|
SUB_NOT_SOUND) = range(6)
|
||||||
SND_EVENT, SND_EVENT_DOC, SND_NAME, SND_PATH = range(4)
|
SND_EVENT, SND_EVENT_DOC, SND_NAME, SND_PATH = range(4)
|
||||||
|
|
||||||
|
class GtkUiNotifications(CustomNotifications):
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
CustomNotifications.enable(self)
|
||||||
|
self.register_custom_blink_notification(
|
||||||
|
"TorrentFinishedEvent", self._on_torrent_finished_event_blink
|
||||||
|
)
|
||||||
|
self.register_custom_sound_notification(
|
||||||
|
"TorrentFinishedEvent", self._on_torrent_finished_event_sound
|
||||||
|
)
|
||||||
|
self.register_custom_popup_notification(
|
||||||
|
"TorrentFinishedEvent", self._on_torrent_finished_event_popup
|
||||||
|
)
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.deregister_custom_blink_notification("TorrentFinishedEvent")
|
||||||
|
self.deregister_custom_sound_notification("TorrentFinishedEvent")
|
||||||
|
self.deregister_custom_popup_notification("TorrentFinishedEvent")
|
||||||
|
CustomNotifications.disable(self)
|
||||||
|
|
||||||
|
def register_custom_popup_notification(self, eventtype, handler):
|
||||||
|
"""This is used to register popup notifications for custom event types.
|
||||||
|
|
||||||
|
:param event: the event name
|
||||||
|
:param type: string
|
||||||
|
:param handler: function, to be called when `:param:event` is emitted
|
||||||
|
|
||||||
|
Your handler should return a tuple of (popup_title, popup_contents).
|
||||||
|
"""
|
||||||
|
self._register_custom_provider('popup', eventtype, handler)
|
||||||
|
|
||||||
|
def deregister_custom_popup_notification(self, eventtype):
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
self._register_custom_provider('blink', eventtype, handler)
|
||||||
|
|
||||||
|
def deregister_custom_blink_notification(self, eventtype):
|
||||||
|
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: the event name
|
||||||
|
:type event: string
|
||||||
|
: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.
|
||||||
|
"""
|
||||||
|
self._register_custom_provider('sound', eventtype, handler)
|
||||||
|
|
||||||
|
def deregister_custom_sound_notification(self, eventtype):
|
||||||
|
self._deregister_custom_provider('sound', eventtype)
|
||||||
|
|
||||||
|
def handle_custom_popup_notification(self, result, eventtype):
|
||||||
|
title, message = result
|
||||||
|
return defer.maybeDeferred(self.__popup, title, message)
|
||||||
|
|
||||||
|
def handle_custom_blink_notification(self, result, eventtype):
|
||||||
|
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, eventtype):
|
||||||
|
if isinstance(result, basestring):
|
||||||
|
if not result and eventtype in self.config['custom_sounds']:
|
||||||
|
return defer.maybeDeferred(
|
||||||
|
self.__play_sound, self.config['custom_sounds'][eventtype])
|
||||||
|
return defer.maybeDeferred(self.__play_sound, result)
|
||||||
|
return defer.succeed("Won't play sound. The returned value from the "
|
||||||
|
"custom handler was: %s", result)
|
||||||
|
|
||||||
|
def __blink(self):
|
||||||
|
self.systray.blink(True)
|
||||||
|
return defer.succeed(_("Notification Blink shown"))
|
||||||
|
|
||||||
|
def __popup(self, title='', message=''):
|
||||||
|
import gtk
|
||||||
|
if not self.config['popup_enabled']:
|
||||||
|
return defer.succeed(_("Popup notification is not enabled."))
|
||||||
|
if not POPUP_AVAILABLE:
|
||||||
|
return defer.fail(_("pynotify is not installed"))
|
||||||
|
|
||||||
|
if pynotify.init("Deluge"):
|
||||||
|
icon = gtk.gdk.pixbuf_new_from_file_at_size(
|
||||||
|
deluge.common.get_pixmap("deluge.svg"), 48, 48)
|
||||||
|
self.note = pynotify.Notification(title, message)
|
||||||
|
self.note.set_icon_from_pixbuf(icon)
|
||||||
|
if not self.note.show():
|
||||||
|
err_msg = _("pynotify failed to show notification")
|
||||||
|
log.warning(err_msg)
|
||||||
|
return defer.fail(err_msg)
|
||||||
|
return defer.succeed(_("Notification popup shown"))
|
||||||
|
|
||||||
|
def __play_sound(self, sound_path=''):
|
||||||
|
if not self.config['sound_enabled']:
|
||||||
|
return defer.succeed(_("Sound notification not enabled"))
|
||||||
|
if not SOUND_AVAILABLE:
|
||||||
|
err_msg = _("pygame is not installed")
|
||||||
|
log.warning(err_msg)
|
||||||
|
return defer.fail(err_msg)
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
try:
|
||||||
|
if not sound_path:
|
||||||
|
sound_path = self.config['sound_path']
|
||||||
|
alert_sound = pygame.mixer.music
|
||||||
|
alert_sound.load(sound_path)
|
||||||
|
alert_sound.play()
|
||||||
|
except pygame.error, message:
|
||||||
|
err_msg = _("Sound notification failed %s") % (message)
|
||||||
|
log.warning(err_msg)
|
||||||
|
return defer.fail(err_msg)
|
||||||
|
else:
|
||||||
|
msg = _("Sound notification Success")
|
||||||
|
log.info(msg)
|
||||||
|
return defer.succeed(msg)
|
||||||
|
|
||||||
|
def _on_torrent_finished_event_blink(self, torrent_id):
|
||||||
|
return True # Yes, Blink
|
||||||
|
|
||||||
|
def _on_torrent_finished_event_sound(self, torrent_id):
|
||||||
|
# Since there's no custom sound hardcoded, just return ''
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def _on_torrent_finished_event_popup(self, torrent_id):
|
||||||
|
d = client.core.get_torrent_status(torrent_id, ["name", "num_files"])
|
||||||
|
d.addCallback(self._on_torrent_finished_event_got_torrent_status)
|
||||||
|
d.addErrback(self._on_torrent_finished_event_torrent_status_failure)
|
||||||
|
return d
|
||||||
|
|
||||||
|
def _on_torrent_finished_event_torrent_status_failure(self, failure):
|
||||||
|
log.debug("Failed to get torrent status to be able to show the popup")
|
||||||
|
|
||||||
|
def _on_torrent_finished_event_got_torrent_status(self, torrent_status):
|
||||||
|
log.debug("Handler for TorrentFinishedEvent GTKUI called. "
|
||||||
|
"Got Torrent Status")
|
||||||
|
title = _("Finished Torrent")
|
||||||
|
message = _("The torrent \"%(name)s\" including %(num_files)i "
|
||||||
|
"has finished downloading.") % torrent_status
|
||||||
|
return title, message
|
||||||
|
|
||||||
class GtkUI(GtkPluginBase, GtkUiNotifications):
|
class GtkUI(GtkPluginBase, GtkUiNotifications):
|
||||||
def __init__(self, plugin_name):
|
def __init__(self, plugin_name):
|
||||||
|
|
|
@ -45,8 +45,12 @@ __author_email__ = "ufs@ufsoft.org"
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
__url__ = "http://dev.deluge-torrent.org/"
|
__url__ = "http://dev.deluge-torrent.org/"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
__description__ = ""
|
__description__ = "Plugin which provides notifications to Deluge."
|
||||||
__long_description__ = """"""
|
__long_description__ = __description__ + """\
|
||||||
|
Email, Popup, Blink and Sound notifications are supported.
|
||||||
|
The plugin also allows other plugins to make use of itself for their own custom
|
||||||
|
notifications.
|
||||||
|
"""
|
||||||
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
|
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
|
Loading…
Reference in New Issue