Moved notifications to plugins (including sound)

This commit is contained in:
Dmitri Bogomolov 2017-03-10 01:00:54 +02:00
parent 24a9dc3b37
commit ef8f40ccc4
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
5 changed files with 160 additions and 109 deletions

View File

@ -254,7 +254,9 @@ if __name__ == "__main__":
install_requires=installRequires,
extras_require={
'qrcode': ['qrcode'],
'pyopencl': ['pyopencl']
'pyopencl': ['pyopencl'],
'notify2': ['pygobject', 'notify2'],
'sound:platform_system=="Windows"': ['winsound']
},
classifiers=[
"License :: OSI Approved :: MIT License"
@ -278,6 +280,13 @@ if __name__ == "__main__":
'popMenuYourIdentities.qrcode = '
'pybitmessage.plugins.qrcodeui [qrcode]'
],
'notification.message': [
'notify2 = pybitmessage.plugins.notification_notify2'
'[notify2]'
],
'notification.sound': [
'fallback = pybitmessage.plugins.sound_playfile [sound]'
],
# 'console_scripts': [
# 'pybitmessage = pybitmessage.bitmessagemain:main'
# ]

View File

@ -4,8 +4,6 @@ try:
import gi
gi.require_version('MessagingMenu', '1.0')
from gi.repository import MessagingMenu
gi.require_version('Notify', '0.7')
from gi.repository import Notify
withMessagingMenu = True
except (ImportError, ValueError):
MessagingMenu = None
@ -62,9 +60,8 @@ import platform
import textwrap
import debug
import random
import subprocess
import string
import datetime
from datetime import datetime, timedelta
from helper_sql import *
import helper_search
import l10n
@ -153,7 +150,7 @@ class MyForm(settingsmixin.SMainWindow):
SOUND_CONNECTION_GREEN = 5
# the last time that a message arrival sound was played
lastSoundTime = datetime.datetime.now() - datetime.timedelta(days=1)
lastSoundTime = datetime.now() - timedelta(days=1)
# the maximum frequency of message sounds in seconds
maxSoundFrequencySec = 60
@ -1368,39 +1365,41 @@ class MyForm(settingsmixin.SMainWindow):
# returns true if the given sound category is a connection sound
# rather than a received message sound
def isConnectionSound(self, category):
if (category is self.SOUND_CONNECTED or
category is self.SOUND_DISCONNECTED or
category is self.SOUND_CONNECTION_GREEN):
return True
return False
return category in (
self.SOUND_CONNECTED,
self.SOUND_DISCONNECTED,
self.SOUND_CONNECTION_GREEN
)
# play a sound
def playSound(self, category, label):
# filename of the sound to be played
soundFilename = None
# whether to play a sound or not
play = True
def _choose_ext(basename):
for ext in ('.wav', '.mp3', '.oga'):
if os.path.isfile(basename + ext):
return ext
# if the address had a known label in the address book
if label is not None:
if label:
# Does a sound file exist for this particular contact?
if (os.path.isfile(state.appdata + 'sounds/' + label + '.wav') or
os.path.isfile(state.appdata + 'sounds/' + label + '.mp3')):
soundFilename = state.appdata + 'sounds/' + label
ext = _choose_ext(soundFilename)
if not ext:
soundFilename = None
if soundFilename is None:
# Avoid making sounds more frequently than the threshold.
# This suppresses playing sounds repeatedly when there
# are many new messages
if (soundFilename is None and
not self.isConnectionSound(category)):
if not self.isConnectionSound(category):
# elapsed time since the last sound was played
dt = datetime.datetime.now() - self.lastSoundTime
dt = datetime.now() - self.lastSoundTime
# suppress sounds which are more frequent than the threshold
if dt.total_seconds() < self.maxSoundFrequencySec:
play = False
return
if soundFilename is None:
# the sound is for an address which exists in the address book
if category is self.SOUND_KNOWN:
soundFilename = state.appdata + 'sounds/known'
@ -1417,73 +1416,53 @@ class MyForm(settingsmixin.SMainWindow):
elif category is self.SOUND_CONNECTION_GREEN:
soundFilename = state.appdata + 'sounds/green'
if soundFilename is not None and play is True:
if soundFilename is None:
return
if not self.isConnectionSound(category):
# record the last time that a received message sound was played
self.lastSoundTime = datetime.datetime.now()
self.lastSoundTime = datetime.now()
# if not wav then try mp3 format
if not os.path.isfile(soundFilename + '.wav'):
soundFilename = soundFilename + '.mp3'
else:
soundFilename = soundFilename + '.wav'
try: # try already known format
soundFilename += ext
except (TypeError, NameError):
ext = _choose_ext(soundFilename)
if not ext:
return
if os.path.isfile(soundFilename):
if 'linux' in sys.platform:
# Note: QSound was a nice idea but it didn't work
if '.mp3' in soundFilename:
gst_available=False
try:
subprocess.call(["gst123", soundFilename],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
gst_available=True
except:
logger.warning("WARNING: gst123 must be installed in order to play mp3 sounds")
if not gst_available:
try:
subprocess.call(["mpg123", soundFilename],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
gst_available=True
except:
logger.warning("WARNING: mpg123 must be installed in order to play mp3 sounds")
else:
try:
subprocess.call(["aplay", soundFilename],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
except:
logger.warning("WARNING: aplay must be installed in order to play WAV sounds")
elif sys.platform[0:3] == 'win':
# use winsound on Windows
import winsound
winsound.PlaySound(soundFilename, winsound.SND_FILENAME)
soundFilename += ext
self._player(soundFilename)
# initialise the message notifier
def notifierInit(self):
if withMessagingMenu:
Notify.init('pybitmessage')
# shows a notification
def notifierShow(self, title, subtitle, fromCategory, label):
self.playSound(fromCategory, label)
if withMessagingMenu:
n = Notify.Notification.new(
title, subtitle, 'notification-message-email')
try:
n.show()
except:
# n.show() has been known to throw this exception:
# gi._glib.GError: GDBus.Error:org.freedesktop.Notifications.
# MaxNotificationsExceeded: Exceeded maximum number of
# notifications
pass
return
else:
def _simple_notify(
title, subtitle, category, label=None, icon=None):
self.tray.showMessage(title, subtitle, 1, 2000)
self._notifier = _simple_notify
self._player = QtGui.QSound.play
if not get_plugins:
return
for plugin in get_plugins('notification.message'):
self._notifier = plugin
break
if not QtGui.QSound.isAvailable():
for plugin in get_plugins('notification.sound'):
self._player = plugin
break
else:
logger.warning("No sound player plugin found!")
def notifierShow(
self, title, subtitle, category, label=None, icon=None):
self.playSound(category, label)
self._notifier(
unicode(title), unicode(subtitle), category, label, icon)
# tree
def treeWidgetKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentTreeWidget())
@ -1663,15 +1642,18 @@ class MyForm(settingsmixin.SMainWindow):
def setStatusIcon(self, color):
# print 'setting status icon color'
_notifications_enabled = not BMConfigParser().getboolean(
'bitmessagesettings', 'hidetrayconnectionnotifications')
if color == 'red':
self.pushButtonStatusIcon.setIcon(
QIcon(":/newPrefix/images/redicon.png"))
shared.statusIconColor = 'red'
# if the connection is lost then show a notification
if self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
self.notifierShow('Bitmessage', unicode(_translate(
"MainWindow", "Connection lost").toUtf8(),'utf-8'),
self.SOUND_DISCONNECTED, None)
if self.connected and _notifications_enabled:
self.notifierShow(
'Bitmessage',
_translate("MainWindow", "Connection lost"),
self.SOUND_DISCONNECTED)
if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \
BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none":
self.statusBar().showMessage(_translate(
@ -1689,10 +1671,11 @@ class MyForm(settingsmixin.SMainWindow):
":/newPrefix/images/yellowicon.png"))
shared.statusIconColor = 'yellow'
# if a new connection has been established then show a notification
if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
self.notifierShow('Bitmessage', unicode(_translate(
"MainWindow", "Connected").toUtf8(),'utf-8'),
self.SOUND_CONNECTED, None)
if not self.connected and _notifications_enabled:
self.notifierShow(
'Bitmessage',
_translate("MainWindow", "Connected"),
self.SOUND_CONNECTED)
self.connected = True
if self.actionStatus is not None:
@ -1705,10 +1688,11 @@ class MyForm(settingsmixin.SMainWindow):
self.pushButtonStatusIcon.setIcon(
QIcon(":/newPrefix/images/greenicon.png"))
shared.statusIconColor = 'green'
if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
self.notifierShow('Bitmessage', unicode(_translate(
"MainWindow", "Connected").toUtf8(),'utf-8'),
self.SOUND_CONNECTION_GREEN, None)
if not self.connected and _notifications_enabled:
self.notifierShow(
'Bitmessage',
_translate("MainWindow", "Connected"),
self.SOUND_CONNECTION_GREEN)
self.connected = True
if self.actionStatus is not None:
@ -2253,8 +2237,14 @@ class MyForm(settingsmixin.SMainWindow):
else:
acct = ret
self.propagateUnreadCount(acct.address)
if BMConfigParser().getboolean('bitmessagesettings', 'showtraynotifications'):
self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None)
if BMConfigParser().getboolean(
'bitmessagesettings', 'showtraynotifications'):
self.notifierShow(
_translate("MainWindow", "New Message"),
_translate("MainWindow", "From %1").arg(
unicode(acct.fromLabel, 'utf-8')),
self.SOUND_UNKNOWN
)
if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address):
# Ubuntu should notify of new message irespective of whether it's in current message list or not
self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel)

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
Notify.init('pybitmessage')
def connect_plugin(title, subtitle, category, label, icon):
if not icon:
icon = 'mail-message-new' if category == 2 else 'pybitmessage'
Notify.Notification.new(title, subtitle, icon).show()

View File

@ -3,10 +3,12 @@
import pkg_resources
def get_plugins(group, point, name=None):
def get_plugins(group, point='', name=None):
for plugin in pkg_resources.iter_entry_points(group):
if plugin.name.startswith(point):
if plugin.name == name or plugin.name.startswith(point):
try:
yield plugin.load().connect_plugin
except (AttributeError, pkg_resources.DistributionNotFound):
except (AttributeError,
pkg_resources.DistributionNotFound,
pkg_resources.UnknownExtra):
continue

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
try:
import winsound
def connect_plugin(sound_file):
winsound.PlaySound(sound_file, winsound.SND_FILENAME)
except ImportError:
import os
import subprocess
play_cmd = {}
def connect_plugin(sound_file):
global play_cmd
ext = os.path.splitext(sound_file)[-1]
try:
subprocess.call([play_cmd[ext], sound_file])
return
except (KeyError, AttributeError):
pass
programs = ['gst123']
if ext == '.wav':
programs.append('aplay')
elif ext == '.mp3':
programs += ['mpg123', 'mpg321', 'mpg321-mpg123']
for cmd in programs:
try:
subprocess.call([cmd, sound_file])
except OSError:
pass # log here!
else:
play_cmd[ext] = cmd
break