Browse Source

Ignore Growl timeout. fixes #1240

pull/1244/head
Ruud 13 years ago
parent
commit
ca08287cff
  1. 7
      couchpotato/core/notifications/growl/main.py
  2. 100
      libs/gntp/__init__.py
  3. 109
      libs/gntp/notifier.py

7
couchpotato/core/notifications/growl/main.py

@ -37,8 +37,11 @@ class Growl(Notification):
)
self.growl.register()
self.registered = True
except:
log.error('Failed register of growl: %s', traceback.format_exc())
except Exception, e:
if 'timed out' in str(e):
self.registered = True
else:
log.error('Failed register of growl: %s', traceback.format_exc())
def notify(self, message = '', data = {}, listener = None):
if self.isDisabled(): return

100
libs/gntp/__init__.py

@ -1,8 +1,9 @@
import hashlib
import re
import hashlib
import time
import StringIO
__version__ = '0.6'
__version__ = '0.8'
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
GNTP_INFO_LINE = re.compile(
@ -19,7 +20,7 @@ GNTP_INFO_LINE_SHORT = re.compile(
GNTP_HEADER = re.compile('([\w-]+):(.+)')
GNTP_EOL = u'\r\n'
GNTP_EOL = '\r\n'
class BaseError(Exception):
@ -43,6 +44,14 @@ class UnsupportedError(BaseError):
errordesc = 'Currently unsupported by gntp.py'
class _GNTPBuffer(StringIO.StringIO):
"""GNTP Buffer class"""
def writefmt(self, message = "", *args):
"""Shortcut function for writing GNTP Headers"""
self.write((message % args).encode('utf8', 'replace'))
self.write(GNTP_EOL)
class _GNTPBase(object):
"""Base initilization
@ -206,8 +215,8 @@ class _GNTPBase(object):
if not match:
continue
key = match.group(1).strip()
val = match.group(2).strip()
key = unicode(match.group(1).strip(), 'utf8', 'replace')
val = unicode(match.group(2).strip(), 'utf8', 'replace')
dict[key] = val
return dict
@ -217,6 +226,15 @@ class _GNTPBase(object):
else:
self.headers[key] = unicode('%s' % value, 'utf8', 'replace')
def add_resource(self, data):
"""Add binary resource
:param string data: Binary Data
"""
identifier = hashlib.md5(data).hexdigest()
self.resources[identifier] = data
return 'x-growl-resource://%s' % identifier
def decode(self, data, password = None):
"""Decode GNTP Message
@ -229,19 +247,30 @@ class _GNTPBase(object):
self.headers = self._parse_dict(parts[0])
def encode(self):
"""Encode a GNTP Message
"""Encode a generic GNTP Message
:return string: Encoded GNTP Message ready to be sent
:return string: GNTP Message ready to be sent
"""
self.validate()
message = self._format_info() + GNTP_EOL
buffer = _GNTPBuffer()
buffer.writefmt(self._format_info())
#Headers
for k, v in self.headers.iteritems():
message += u'%s: %s%s' % (k, v, GNTP_EOL)
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
message += GNTP_EOL
return message
#Resources
for resource, data in self.resources.iteritems():
buffer.writefmt('Identifier: %s', resource)
buffer.writefmt('Length: %d', len(data))
buffer.writefmt()
buffer.write(data)
buffer.writefmt()
buffer.writefmt()
return buffer.getvalue()
class GNTPRegister(_GNTPBase):
@ -290,7 +319,7 @@ class GNTPRegister(_GNTPBase):
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
@ -319,22 +348,33 @@ class GNTPRegister(_GNTPBase):
:return string: Encoded GNTP Registration message
"""
self.validate()
message = self._format_info() + GNTP_EOL
buffer = _GNTPBuffer()
buffer.writefmt(self._format_info())
#Headers
for k, v in self.headers.iteritems():
message += u'%s: %s%s' % (k, v, GNTP_EOL)
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
#Notifications
if len(self.notifications) > 0:
for notice in self.notifications:
message += GNTP_EOL
for k, v in notice.iteritems():
message += u'%s: %s%s' % (k, v, GNTP_EOL)
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
#Resources
for resource, data in self.resources.iteritems():
buffer.writefmt('Identifier: %s', resource)
buffer.writefmt('Length: %d', len(data))
buffer.writefmt()
buffer.write(data)
buffer.writefmt()
buffer.writefmt()
message += GNTP_EOL
return message
return buffer.getvalue()
class GNTPNotice(_GNTPBase):
@ -379,7 +419,7 @@ class GNTPNotice(_GNTPBase):
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
@ -388,21 +428,6 @@ class GNTPNotice(_GNTPBase):
#open('notice.png','wblol').write(notice['Data'])
self.resources[notice.get('Identifier')] = notice
def encode(self):
"""Encode a GNTP Notification Message
:return string: GNTP Notification Message ready to be sent
"""
self.validate()
message = self._format_info() + GNTP_EOL
#Headers
for k, v in self.headers.iteritems():
message += u'%s: %s%s' % (k, v, GNTP_EOL)
message += GNTP_EOL
return message
class GNTPSubscribe(_GNTPBase):
"""Represents a GNTP Subscribe Command
@ -457,7 +482,8 @@ class GNTPError(_GNTPBase):
self.add_header('Error-Description', errordesc)
def error(self):
return self.headers['Error-Code'], self.headers['Error-Description']
return (self.headers.get('Error-Code', None),
self.headers.get('Error-Description', None))
def parse_gntp(data, password = None):

109
libs/gntp/notifier.py

@ -22,43 +22,6 @@ __all__ = [
logger = logging.getLogger(__name__)
def mini(description, applicationName = 'PythonMini', noteType = "Message",
title = "Mini Message", applicationIcon = None, hostname = 'localhost',
password = None, port = 23053, sticky = False, priority = None,
callback = None):
"""Single notification function
Simple notification function in one line. Has only one required parameter
and attempts to use reasonable defaults for everything else
:param string description: Notification message
.. warning::
For now, only URL callbacks are supported. In the future, the
callback argument will also support a function
"""
growl = GrowlNotifier(
applicationName = applicationName,
notifications = [noteType],
defaultNotifications = [noteType],
hostname = hostname,
password = password,
port = port,
)
result = growl.register()
if result is not True:
return result
return growl.notify(
noteType = noteType,
title = title,
description = description,
icon = applicationIcon,
sticky = sticky,
priority = priority,
callback = callback,
)
class GrowlNotifier(object):
"""Helper class to simplfy sending Growl messages
@ -93,10 +56,12 @@ class GrowlNotifier(object):
def _checkIcon(self, data):
'''
Check the icon to see if it's valid
@param data:
@todo Consider checking for a valid URL
If it's a simple URL icon, then we return True. If it's a data icon
then we return False
'''
return data
logger.info('Checking icon')
return data.startswith('http')
def register(self):
"""Send GNTP Registration
@ -112,7 +77,11 @@ class GrowlNotifier(object):
enabled = notification in self.defaultNotifications
register.add_notification(notification, enabled)
if self.applicationIcon:
register.add_header('Application-Icon', self.applicationIcon)
if self._checkIcon(self.applicationIcon):
register.add_header('Application-Icon', self.applicationIcon)
else:
id = register.add_resource(self.applicationIcon)
register.add_header('Application-Icon', id)
if self.password:
register.set_password(self.password, self.passwordHash)
self.add_origin_info(register)
@ -120,7 +89,7 @@ class GrowlNotifier(object):
return self._send('register', register)
def notify(self, noteType, title, description, icon = None, sticky = False,
priority = None, callback = None):
priority = None, callback = None, identifier = None):
"""Send a GNTP notifications
.. warning::
@ -151,11 +120,18 @@ class GrowlNotifier(object):
if priority:
notice.add_header('Notification-Priority', priority)
if icon:
notice.add_header('Notification-Icon', self._checkIcon(icon))
if self._checkIcon(icon):
notice.add_header('Notification-Icon', icon)
else:
id = notice.add_resource(icon)
notice.add_header('Notification-Icon', id)
if description:
notice.add_header('Notification-Text', description)
if callback:
notice.add_header('Notification-Callback-Target', callback)
if identifier:
notice.add_header('Notification-Coalescing-ID', identifier)
self.add_origin_info(notice)
self.notify_hook(notice)
@ -193,9 +169,10 @@ class GrowlNotifier(object):
def subscribe_hook(self, packet):
pass
def _send(self, type, packet):
def _send(self, messagetype, packet):
"""Send the GNTP Packet"""
packet.validate()
data = packet.encode()
logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
@ -203,7 +180,7 @@ class GrowlNotifier(object):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(self.socketTimeout)
s.connect((self.hostname, self.port))
s.send(data.encode('utf8', 'replace'))
s.send(data)
recv_data = s.recv(1024)
while not recv_data.endswith("\r\n\r\n"):
recv_data += s.recv(1024)
@ -212,11 +189,51 @@ class GrowlNotifier(object):
logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
if response.info['messagetype'] == '-OK':
if type(response) == gntp.GNTPOK:
return True
logger.error('Invalid response: %s', response.error())
return response.error()
def mini(description, applicationName = 'PythonMini', noteType = "Message",
title = "Mini Message", applicationIcon = None, hostname = 'localhost',
password = None, port = 23053, sticky = False, priority = None,
callback = None, notificationIcon = None, identifier = None,
notifierFactory = GrowlNotifier):
"""Single notification function
Simple notification function in one line. Has only one required parameter
and attempts to use reasonable defaults for everything else
:param string description: Notification message
.. warning::
For now, only URL callbacks are supported. In the future, the
callback argument will also support a function
"""
growl = notifierFactory(
applicationName = applicationName,
notifications = [noteType],
defaultNotifications = [noteType],
applicationIcon = applicationIcon,
hostname = hostname,
password = password,
port = port,
)
result = growl.register()
if result is not True:
return result
return growl.notify(
noteType = noteType,
title = title,
description = description,
icon = notificationIcon,
sticky = sticky,
priority = priority,
callback = callback,
identifier = identifier,
)
if __name__ == '__main__':
# If we're running this module directly we're likely running it as a test
# so extra debugging is useful

Loading…
Cancel
Save