Bindings/python/Growl.py
author Peter Hosey
Wed Mar 25 04:11:45 2009 -0700 (2009-03-25)
changeset 4182 69b5347d8262
parent 3928 8a7f66f30c17
child 4183 2e72fc363066
permissions -rw-r--r--
Patch by Charles Lepple to fix two of the assertions in our Python bindings.

In Python, assert is a statement, not a function. With the parentheses, Python takes the parentheses and everything between them as a tuple, and uses this as the condition; since it contains two items, it is not empty, which means it is true, which means the assertion would always pass.

The fix is to remove the parentheses. Then, the assertion statement has its usual two arguments (formerly the items in the tuple).
aranor@203
     1
"""
aranor@692
     2
A Python module that enables posting notifications to the Growl daemon.
tick@1181
     3
See <http://growl.info/> for more information.
aranor@203
     4
"""
toby@1004
     5
__version__ = "0.7" 
toby@1004
     6
__author__ = "Mark Rowe <bdash@users.sourceforge.net>"
toby@1004
     7
__copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
toby@1004
     8
__contributors__ = ["Ingmar J Stein (Growl Team)", 
boredzo@3900
     9
					"Rui Carmo (http://the.taoofmac.com)",
boredzo@3900
    10
					"Jeremy Rossi <jeremy@jeremyrossi.com>",
boredzo@3679
    11
					"Peter Hosey <http://boredzo.org/> (Growl Team)",
boredzo@3900
    12
				   ]
toby@1004
    13
boredzo@3679
    14
import _growl
toby@1004
    15
import types
toby@1004
    16
import struct
toby@1004
    17
import md5
toby@1004
    18
import socket
toby@1004
    19
toby@1004
    20
GROWL_UDP_PORT=9887
toby@1004
    21
GROWL_PROTOCOL_VERSION=1
toby@1004
    22
GROWL_TYPE_REGISTRATION=0
toby@1004
    23
GROWL_TYPE_NOTIFICATION=1
toby@1004
    24
toby@1004
    25
GROWL_APP_NAME="ApplicationName"
toby@1004
    26
GROWL_APP_ICON="ApplicationIcon"
toby@1004
    27
GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
toby@1004
    28
GROWL_NOTIFICATIONS_ALL="AllNotifications"
toby@1004
    29
GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
toby@1004
    30
toby@1004
    31
GROWL_NOTIFICATION_NAME="NotificationName"
toby@1004
    32
GROWL_NOTIFICATION_TITLE="NotificationTitle"
toby@1004
    33
GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
toby@1004
    34
GROWL_NOTIFICATION_ICON="NotificationIcon"
toby@1004
    35
GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
toby@1004
    36
GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
boredzo@3900
    37
		
toby@1004
    38
GROWL_NOTIFICATION_STICKY="NotificationSticky"
toby@1004
    39
toby@1004
    40
GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
toby@1004
    41
GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
ingmarstein@2603
    42
GROWL_NOTIFICATION="GrowlNotification"
toby@1004
    43
GROWL_SHUTDOWN="GrowlShutdown"
toby@1004
    44
GROWL_PING="Honey, Mind Taking Out The Trash"
toby@1004
    45
GROWL_PONG="What Do You Want From Me, Woman"
toby@1004
    46
GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
toby@1004
    47
boredzo@3900
    48
	
aranor@692
    49
growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
aranor@692
    50
toby@1004
    51
class netgrowl:
boredzo@3900
    52
	"""Builds a Growl Network Registration packet.
boredzo@3900
    53
	   Defaults to emulating the command-line growlnotify utility."""
boredzo@3900
    54
boredzo@3900
    55
	__notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
boredzo@3900
    56
boredzo@3900
    57
	def __init__(self, hostname, password ):
boredzo@3900
    58
		self.hostname = hostname
boredzo@3900
    59
		self.password = password
boredzo@3900
    60
		self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
boredzo@3900
    61
boredzo@3900
    62
	def send(self, data):
boredzo@3900
    63
		self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
boredzo@3900
    64
		
boredzo@3900
    65
	def PostNotification(self, userInfo):
boredzo@3900
    66
		if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
boredzo@3900
    67
			priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
boredzo@3900
    68
		else:
boredzo@3900
    69
			priority = 0
boredzo@3900
    70
		if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
boredzo@3900
    71
			sticky = userInfo[GROWL_NOTIFICATION_STICKY]
boredzo@3900
    72
		else:
rudy@3928
    73
			sticky = False
boredzo@3900
    74
		data = self.encodeNotify(userInfo[GROWL_APP_NAME],
boredzo@3900
    75
								 userInfo[GROWL_NOTIFICATION_NAME],
boredzo@3900
    76
								 userInfo[GROWL_NOTIFICATION_TITLE],
boredzo@3900
    77
								 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
boredzo@3900
    78
								 priority,
boredzo@3900
    79
								 sticky)
boredzo@3900
    80
		return self.send(data)
boredzo@3900
    81
boredzo@3900
    82
	def PostRegistration(self, userInfo):
boredzo@3900
    83
		data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
boredzo@3900
    84
									   userInfo[GROWL_NOTIFICATIONS_ALL],
boredzo@3900
    85
									   userInfo[GROWL_NOTIFICATIONS_DEFAULT])
boredzo@3900
    86
		return self.send(data)
boredzo@3900
    87
boredzo@3900
    88
	def encodeRegistration(self, application, notifications, defaultNotifications):
boredzo@3900
    89
		data = struct.pack("!BBH",
boredzo@3900
    90
						   GROWL_PROTOCOL_VERSION,
boredzo@3900
    91
						   GROWL_TYPE_REGISTRATION,
boredzo@3900
    92
						   len(application) )
boredzo@3900
    93
		data += struct.pack("BB",
boredzo@3900
    94
							len(notifications),
boredzo@3900
    95
							len(defaultNotifications) )
boredzo@3900
    96
		data += application
boredzo@3900
    97
		for i in notifications:
boredzo@3900
    98
			encoded = i.encode("utf-8")
boredzo@3900
    99
			data += struct.pack("!H", len(encoded))
boredzo@3900
   100
			data += encoded
boredzo@3900
   101
		for i in defaultNotifications:
boredzo@3900
   102
			data += struct.pack("B", i)
boredzo@3900
   103
		return self.encodePassword(data)
boredzo@3900
   104
boredzo@3900
   105
	def encodeNotify(self, application, notification, title, description,
boredzo@3900
   106
					 priority = 0, sticky = False):
boredzo@3900
   107
boredzo@3900
   108
		application  = application.encode("utf-8")
boredzo@3900
   109
		notification = notification.encode("utf-8")
boredzo@3900
   110
		title		= title.encode("utf-8")
boredzo@3900
   111
		description  = description.encode("utf-8")
boredzo@3900
   112
		flags = (priority & 0x07) * 2
boredzo@3900
   113
		if priority < 0: 
boredzo@3900
   114
			flags |= 0x08
boredzo@3900
   115
		if sticky: 
boredzo@3900
   116
			flags = flags | 0x0001
boredzo@3900
   117
		data = struct.pack("!BBHHHHH",
boredzo@3900
   118
						   GROWL_PROTOCOL_VERSION,
boredzo@3900
   119
						   GROWL_TYPE_NOTIFICATION,
boredzo@3900
   120
						   flags,
boredzo@3900
   121
						   len(notification),
boredzo@3900
   122
						   len(title),
boredzo@3900
   123
						   len(description),
boredzo@3900
   124
						   len(application) )
boredzo@3900
   125
		data += notification
boredzo@3900
   126
		data += title
boredzo@3900
   127
		data += description
boredzo@3900
   128
		data += application
boredzo@3900
   129
		return self.encodePassword(data)
boredzo@3900
   130
boredzo@3900
   131
	def encodePassword(self, data):
boredzo@3900
   132
		checksum = md5.new()
boredzo@3900
   133
		checksum.update(data)
boredzo@3900
   134
		if self.password:
boredzo@3900
   135
		   checksum.update(self.password)
boredzo@3900
   136
		data += checksum.digest()
boredzo@3900
   137
		return data
toby@1004
   138
aranor@1007
   139
class _ImageHook(type):
boredzo@3900
   140
	def __getattribute__(self, attr):
boredzo@3900
   141
		global Image
boredzo@3900
   142
		if Image is self:
boredzo@3900
   143
			from _growlImage import Image
boredzo@3900
   144
boredzo@3900
   145
		return getattr(Image, attr)
aranor@1007
   146
aranor@1007
   147
class Image(object):
boredzo@3900
   148
	__metaclass__ = _ImageHook
aranor@1007
   149
aranor@1007
   150
class _RawImage(object):
boredzo@3900
   151
	def __init__(self, data):  self.rawImageData = data
toby@1004
   152
aranor@203
   153
class GrowlNotifier(object):
boredzo@3900
   154
	"""
boredzo@3900
   155
	A class that abstracts the process of registering and posting
boredzo@3900
   156
	notifications to the Growl daemon.
boredzo@3900
   157
boredzo@3900
   158
	You can either pass `applicationName', `notifications',
boredzo@3900
   159
	`defaultNotifications' and `applicationIcon' to the constructor
boredzo@3900
   160
	or you may define them as class-level variables in a sub-class.
boredzo@3900
   161
boredzo@3900
   162
	`defaultNotifications' is optional, and defaults to the value of
boredzo@3900
   163
	`notifications'.  `applicationIcon' is also optional but defaults
boredzo@3900
   164
	to a pointless icon so is better to be specified.
boredzo@3900
   165
	"""
boredzo@3900
   166
boredzo@3900
   167
	applicationName = 'GrowlNotifier'
boredzo@3900
   168
	notifications = []
boredzo@3900
   169
	defaultNotifications = []
boredzo@3900
   170
	applicationIcon = None
boredzo@3900
   171
	_notifyMethod = _growl
boredzo@3900
   172
boredzo@3900
   173
	def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
boredzo@3901
   174
		if applicationName:
boredzo@3901
   175
			self.applicationName = applicationName
Peter@4182
   176
		assert self.applicationName, 'An application name is required.'
boredzo@3901
   177
boredzo@3901
   178
		if notifications:
boredzo@3901
   179
			self.notifications = list(notifications)
Peter@4182
   180
		assert self.notifications, 'A sequence of one or more notification names is required.'
boredzo@3901
   181
boredzo@3900
   182
		if defaultNotifications is not None:
boredzo@3900
   183
			self.defaultNotifications = list(defaultNotifications)
boredzo@3901
   184
		elif not self.defaultNotifications:
boredzo@3900
   185
			self.defaultNotifications = list(self.notifications)
boredzo@3900
   186
boredzo@3900
   187
		if applicationIcon is not None:
boredzo@3900
   188
			self.applicationIcon = self._checkIcon(applicationIcon)
boredzo@3901
   189
		elif self.applicationIcon is not None:
boredzo@3901
   190
			self.applicationIcon = self._checkIcon(self.applicationIcon)
boredzo@3900
   191
boredzo@3900
   192
		if hostname is not None and password is not None:
boredzo@3900
   193
			self._notifyMethod = netgrowl(hostname, password)
boredzo@3900
   194
		elif hostname is not None or password is not None:
boredzo@3900
   195
			raise KeyError, "Hostname and Password are both required for a network notification"
boredzo@3900
   196
boredzo@3900
   197
	def _checkIcon(self, data):
boredzo@3900
   198
		if isinstance(data, str):
boredzo@3900
   199
			return _RawImage(data)
boredzo@3900
   200
		else:
boredzo@3900
   201
			return data
boredzo@3900
   202
boredzo@3900
   203
	def register(self):
boredzo@3900
   204
		if self.applicationIcon is not None:
boredzo@3900
   205
			self.applicationIcon = self._checkIcon(self.applicationIcon)
boredzo@3900
   206
boredzo@3900
   207
		regInfo = {GROWL_APP_NAME: self.applicationName,
boredzo@3900
   208
				   GROWL_NOTIFICATIONS_ALL: self.notifications,
boredzo@3900
   209
				   GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
boredzo@3900
   210
				   GROWL_APP_ICON:self.applicationIcon,
boredzo@3900
   211
				  }
boredzo@3900
   212
		self._notifyMethod.PostRegistration(regInfo)
boredzo@3900
   213
boredzo@3900
   214
	def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
boredzo@3900
   215
		assert noteType in self.notifications
boredzo@3900
   216
		notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
boredzo@3900
   217
					  GROWL_APP_NAME: self.applicationName,
boredzo@3900
   218
					  GROWL_NOTIFICATION_TITLE: title,
boredzo@3900
   219
					  GROWL_NOTIFICATION_DESCRIPTION: description,
boredzo@3900
   220
					 }
boredzo@3900
   221
		if sticky:
boredzo@3900
   222
			notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
boredzo@3900
   223
boredzo@3900
   224
		if priority is not None:
boredzo@3900
   225
			notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
boredzo@3900
   226
boredzo@3900
   227
		if icon:
boredzo@3900
   228
			notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
boredzo@3900
   229
boredzo@3900
   230
		self._notifyMethod.PostNotification(notifyInfo)