]> git.pld-linux.org Git - packages/ekg2-script-pynotif.git/blob - pynotif.py
- up to 1.0
[packages/ekg2-script-pynotif.git] / pynotif.py
1 # vim:fileencoding=utf-8:sw=4
2
3 #   This program is free software: you can redistribute it and/or modify
4 #   it under the terms of the GNU General Public License as published by
5 #   the Free Software Foundation, either version 3 of the License, or
6 #   (at your option) any later version.
7 #
8 #   This program is distributed in the hope that it will be useful,
9 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #   GNU General Public License for more details.
12 #
13 #   You should have received a copy of the GNU General Public License
14 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 #   Copyright (c) 2009 by Paweł Tomak <satherot (at) gmail (dot) com>
17 #   Copyright (c) 2009 by Paweł Zuzelski <pawelz.pld-linux.org>
18
19 import ekg
20 import time
21 import pynotify
22 import re
23 import sys
24 import glib
25
26 TIMEOUT_STATUS=3500
27 TIMEOUT_MSG=3500
28
29 def removeTextFormatting(text):
30     """
31     Removes advance text formatting from notifications.
32
33     Converts html reserved characters (&, ", < and >) to xml entities.
34     Removes ANSII color strings.
35     """
36     # Remove html tags
37     reg = re.compile("&")
38     text = reg.sub("&#38;", text)
39     reg = re.compile('"')
40     text = reg.sub("&#34;", text)
41     reg = re.compile("<")
42     text = reg.sub("&#60;", text)
43     reg = re.compile(">")
44     text = reg.sub("&#62;", text)
45     # Remove terminal color codes
46     reg = re.compile("\e\[[0-9]+;[0-9]+m")
47     text = reg.sub("", text)
48     reg = re.compile("\e\[[0-9]+m")
49     text = reg.sub("", text)
50     return text
51
52 def parseMeCommand(text, user):
53     """
54     Interpretes /me command as defined in JEP-0245
55
56     If text begins with '/me ', substitute it with '* user '
57     """
58
59     if (text[:4] == "/me "):
60         text = "* " + user + text[3:]
61
62     return text
63
64 def catchURL(text):
65     """
66     Converts URLs to html "a href" tags, so they will be clickable if
67     notification daemon supports basic html.
68     """
69     reg = re.compile("((news|telnet|nttp|file|http|ftp|https)://[^ ]+|www.[^ ]+)")
70     if len(reg.findall(text)):
71         text = reg.sub(r'<a href="\1">\1</a>', text)
72         return [1, text]
73     else:
74         return [0, text]
75
76 def transStatus(status):
77     return {
78             'avail': 'dostepny',
79             'away': 'zaraz wracam',
80             'blocking': 'BLOKUJE',
81             'error': 'BLAD STATUSU!!',
82             'ffc': 'chetny do rozmowy',
83             'chat': 'chetny do rozmowy',
84             'dnd': 'nie przeszkadzac',
85             'xa': 'bardzo zajety',
86             'notavail': 'niedostepny',
87             'unknown': 'nieznany',
88             }[status]
89
90 def getUrgency(title, text):
91     """
92     Performs notify:urgency_critical_regexp and notify:urgency_normal_regexp
93     matching on "title&text". "&" character was chosen as separator, because
94     acording to RFC3920, Apendix A, use of this character is forbiden i JID,
95     so it should not apear in title.
96
97     If notify:urgency_critical_regexp matches, returns urgency CRITICAL. If
98     not, tries to match notify:urgency_normal_regexp, on success return
99     urgency NORMAL. Finally return urgency LOW.
100
101     Default values are "^$" wich are never matched, as string always contain
102     at least single "&" character.
103     """
104     
105     message=title+"&"+text
106     
107     urgencyCritical = re.compile(ekg.config["notify:urgency_critical_regexp"])
108     if (urgencyCritical.match(message)):
109         return pynotify.URGENCY_CRITICAL
110     
111     urgencyNormal = re.compile(ekg.config["notify:urgency_normal_regexp"])
112     if (urgencyNormal.match(message)):
113         return pynotify.URGENCY_NORMAL
114     
115     return pynotify.URGENCY_LOW
116
117 def displayNotify(title, text, timeout, type):
118     """
119     Sends notification to dbus org.freedesktop.Notifications service using
120     pynotify python library.
121     """
122     if not pynotify.init("EkgNotif"):
123         ekg.echo("you don't seem to have pynotify installed")
124         return 0
125     if ekg.config["notify:catch_url"] != "0":
126         l = catchURL(text)
127         if l[0]:
128             text = l[1]
129             timeout = int(ekg.config["notify:catch_url_timeout"])
130     n = pynotify.Notification(title, text, type)
131
132     n.set_timeout(timeout)
133     n.set_urgency(getUrgency(title, text))
134
135     # Most probably glib.GError is:
136     # The name org.freedesktop.Notifications was not provided by any
137     # .service files
138     # Catch this exception and print information in debug window.
139     # Sometimes I
140     # do not have org.freedesktop.Notifications registered and
141     # I do not want
142     # error messages in chat window.
143     # Or logs buffer has overflowed ;/
144     try:
145         n.show()
146     except glib.GError as e:
147         ekg.debug("pynotif: " + str(e))
148
149     return 1
150
151 def notifyStatus(session, uid, status, descr):
152     """
153     Display status change notifications, but first check if status change
154     notifications are enabled, then check if session and uids match
155     ignore_{sessions,uids}_regexp.
156
157     This function is bound to protocol-status handler
158     """
159     if ekg.config["notify:status_notify"] == "0":
160         return 1
161     if (ekg.config["notify:ignore_sessions_regexp"]):
162       regexp = re.compile(ekg.config["notify:ignore_sessions_regexp"])
163       if regexp.match(session):
164           return 1
165     if (ekg.config["notify:ignore_uids_regexp"]):
166       regexp = re.compile(ekg.config["notify:ignore_uids_regexp"])
167       if regexp.match(uid):
168           return 1
169     regexp = re.compile('.*' + session + '.*')
170     if regexp.match(uid):
171         ekg.debug("Zmienil sie status sesji: %s. Nie zostal on zmieniony przez ten program. Sprawdz to, jesli nie zmieniales statusu jakims innym programem" % session)
172         return 1
173     sesja = ekg.session_get(session)
174     regexp = re.compile('([a-z]{2,4}:[^/]+)')
175     regexp = regexp.match(uid)
176     regexp = regexp.group()
177     try:
178         user = sesja.user_get(regexp)
179     except KeyError:
180         ekg.debug("Nie znalazlem uzytkownika %s." % uid)
181         user = "Empty"
182     status = transStatus(status)
183     if user == "Empty":
184         nick = regexp
185     else:
186         nick = user.nickname or user.uid or "Empty"
187     s = status or "Empty"
188     s = removeTextFormatting(s)
189     text = "<b>" + nick + "</b> zmienil status na <b>" + s + "</b>"
190     if descr:
191         descr = removeTextFormatting(descr)
192         text = text + ":\n" + descr + "\n"
193     return displayNotify(session, text, TIMEOUT_STATUS, ekg.config["notify:icon_status"])
194
195 def notifyMessage(session, uid, type, text, stime, ignore_level):
196     """
197     Display message notifications, but first check if message notifications
198     are enabled, then check if session and uids match
199     ignore_{sessions,uids}_regexp.
200
201     This function is bound to protocol-message handler
202     """
203     if ekg.config["notify:message_notify"] == "0":
204         return 1
205     if (ekg.config["notify:ignore_sessions_regexp"]):
206       regexp = re.compile(ekg.config["notify:ignore_sessions_regexp"])
207       if regexp.match(session):
208           return 1
209     if (ekg.config["notify:ignore_uids_regexp"]):
210       regexp = re.compile(ekg.config["notify:ignore_uids_regexp"])
211       if regexp.match(uid):
212           return 1
213     sesja = ekg.session_get(session)
214     try:
215         user = sesja.user_get(uid)
216     except KeyError:
217         ekg.debug("Nie znalazlem uzytkownika %s." % uid)
218         user = "Empty"
219     if user == None:
220         user = "Empty"
221     if user == "Empty" and ekg.config["notify:message_notify_unknown"] == "0":
222         return 1
223     if user == "Empty":
224         user = uid
225     else:
226         user = user.nickname
227     try:
228         title = user
229     except KeyError:
230         title = uid
231     if (ekg.config["notify:show_timestamps"] == "1"):
232         title = time.strftime("%H:%M:%S", time.localtime(stime)) + " " + title
233     text = removeTextFormatting(text)
234     text = parseMeCommand(text, user)
235     if len(text) > 200:
236         text = text[0:199] + "... >>>\n\n"
237     return displayNotify(title, text, TIMEOUT_MSG, ekg.config["notify:icon_msg"])
238
239 def timeCheck(name, args):
240     global TIMEOUT_MSG
241     global TIMEOUT_STATUS
242     rexp = re.compile('^[0-9]{4,4}')
243     rexp = rexp.findall(args)
244     if len(rexp) == 1:
245         if name == "notify:message_timeout":
246             TIMEOUT_MSG = int(rexp[0])
247             return 1
248         if name == "notify:status_timeout":
249             TIMEOUT_STATUS = int(rexp[0])
250             return 1
251         if name == "notify:catch_url_timeout":
252             return 1
253
254     if name == "notify:message_timeout":
255         ekg.echo("Zmienna %s bedzie pomijana do czasu, az zostanie ustawiona wartosc z zakresu od 1000ms do 9999ms. Jej obecna wartosc to: %i" % (name,TIMEOUT_MSG))
256     elif name == "notify:status_timeout":
257         ekg.echo("Zmienna %s bedzie pomijana do czasu, az zostanie ustawiona wartosc z zakresu od 1000ms do 9999ms. Jej obecna wartosc to: %i" % (name,TIMEOUT_STATUS))
258     elif name == "notify:catch_url_timeout":
259         ekg.echo("Zmienna %s bedzie pomijana do czasu, az zostanie ustawiona wartosc z zakresu od 1000ms do 9999ms. Jej obecna wartosc to: %i" % (name,TIMEOUT_STATUS))
260     return 0
261
262 def notifyTest(name, args):
263     """
264     Sends test notification.
265
266     This function is bound to notify:send command
267     """
268     args = args.split(None, 1)
269     if (len(args) == 0):
270         title="Test"
271     else:
272         title=args[0]
273   
274     if (len(args) <= 1):
275         text="Pięćdziesiąt trzy"
276     else:
277         text = args[1]
278   
279     return displayNotify(title, text, TIMEOUT_MSG, ekg.config["notify:icon_msg"])
280
281 ekg.handler_bind('protocol-status', notifyStatus)
282 ekg.handler_bind("protocol-message-received", notifyMessage)
283 ekg.variable_add("notify:ignore_sessions_regexp", "^irc:")
284 ekg.variable_add("notify:ignore_uids_regexp", "^xmpp:.*@conference\.")
285 ekg.variable_add("notify:urgency_critical_regexp", "^$")
286 ekg.variable_add("notify:urgency_normal_regexp", "^$")
287 ekg.variable_add("notify:icon_status", "dialog-warning")
288 ekg.variable_add("notify:icon_msg", "dialog-warning")
289 ekg.variable_add("notify:message_timeout", "3500", timeCheck)
290 ekg.variable_add("notify:message_notify", "1")
291 ekg.variable_add("notify:message_notify_unknown", "1")
292 ekg.variable_add("notify:show_timestamps", "1")
293 ekg.variable_add("notify:status_timeout", "3500", timeCheck)
294 ekg.variable_add("notify:status_notify", "1")
295 ekg.variable_add("notify:catch_url", "1")
296 ekg.variable_add("notify:catch_url_timeout", "5000", timeCheck)
297 ekg.command_bind("notify:send", notifyTest)
298
299 if int(ekg.config["notify:message_timeout"]) < 1000 or int(ekg.config["notify:message_timeout"]) > 9999:
300     timeCheck("notify:message_timeout", ekg.config["notify:message_timeout"])
301 if int(ekg.config["notify:status_timeout"]) < 1000 or int(ekg.config["notify:status_timeout"]) > 9999:
302     timeCheck("notify:status_timeout", ekg.config["notify:status_timeout"])
303
This page took 0.067764 seconds and 3 git commands to generate.