]>
Commit | Line | Data |
---|---|---|
7c8d37d7 | 1 | # vim:fileencoding=utf-8:sw=4 |
8f9d83b6 | 2 | |
461ea624 | 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> | |
7c8d37d7 | 17 | # Copyright (c) 2009 by Paweł Zuzelski <pawelz.pld-linux.org> |
461ea624 | 18 | |
19 | import ekg | |
20 | import time | |
21 | import pynotify | |
22 | import re | |
23 | import sys | |
7c8d37d7 | 24 | import glib |
461ea624 | 25 | |
26 | TIMEOUT_STATUS=3500 | |
27 | TIMEOUT_MSG=3500 | |
28 | ||
344b33ab | 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 | |
461ea624 | 37 | reg = re.compile("&") |
38 | text = reg.sub("&", text) | |
7c8d37d7 | 39 | reg = re.compile('"') |
40 | text = reg.sub(""", text) | |
461ea624 | 41 | reg = re.compile("<") |
42 | text = reg.sub("<", text) | |
43 | reg = re.compile(">") | |
44 | text = reg.sub(">", text) | |
344b33ab | 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 | ||
461ea624 | 62 | return text |
63 | ||
7c8d37d7 | 64 | def catchURL(text): |
344b33ab | 65 | """ |
66 | Converts URLs to html "a href" tags, so they will be clickable if | |
67 | notification daemon supports basic html. | |
68 | """ | |
7c8d37d7 | 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 | ||
461ea624 | 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', | |
7c8d37d7 | 87 | 'unknown': 'nieznany', |
461ea624 | 88 | }[status] |
89 | ||
344b33ab | 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 | ||
461ea624 | 117 | def displayNotify(title, text, timeout, type): |
344b33ab | 118 | """ |
119 | Sends notification to dbus org.freedesktop.Notifications service using | |
120 | pynotify python library. | |
121 | """ | |
461ea624 | 122 | if not pynotify.init("EkgNotif"): |
123 | ekg.echo("you don't seem to have pynotify installed") | |
124 | return 0 | |
7c8d37d7 | 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"]) | |
461ea624 | 130 | n = pynotify.Notification(title, text, type) |
344b33ab | 131 | |
461ea624 | 132 | n.set_timeout(timeout) |
344b33ab | 133 | n.set_urgency(getUrgency(title, text)) |
7c8d37d7 | 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 | ||
461ea624 | 149 | return 1 |
150 | ||
151 | def notifyStatus(session, uid, status, descr): | |
344b33ab | 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 | """ | |
7c8d37d7 | 159 | if ekg.config["notify:status_notify"] == "0": |
160 | return 1 | |
344b33ab | 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 | |
461ea624 | 169 | regexp = re.compile('.*' + session + '.*') |
7c8d37d7 | 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) | |
461ea624 | 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: | |
7c8d37d7 | 180 | ekg.debug("Nie znalazlem uzytkownika %s." % uid) |
181 | user = "Empty" | |
461ea624 | 182 | status = transStatus(status) |
7c8d37d7 | 183 | if user == "Empty": |
184 | nick = regexp | |
185 | else: | |
186 | nick = user.nickname or user.uid or "Empty" | |
461ea624 | 187 | s = status or "Empty" |
344b33ab | 188 | s = removeTextFormatting(s) |
461ea624 | 189 | text = "<b>" + nick + "</b> zmienil status na <b>" + s + "</b>" |
190 | if descr: | |
344b33ab | 191 | descr = removeTextFormatting(descr) |
461ea624 | 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): | |
344b33ab | 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 | """ | |
7c8d37d7 | 203 | if ekg.config["notify:message_notify"] == "0": |
204 | return 1 | |
344b33ab | 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 | |
461ea624 | 213 | sesja = ekg.session_get(session) |
214 | try: | |
215 | user = sesja.user_get(uid) | |
216 | except KeyError: | |
7c8d37d7 | 217 | ekg.debug("Nie znalazlem uzytkownika %s." % uid) |
218 | user = "Empty" | |
344b33ab | 219 | if user == None: |
220 | user = "Empty" | |
7c8d37d7 | 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 | |
344b33ab | 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) | |
461ea624 | 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 | |
7c8d37d7 | 251 | if name == "notify:catch_url_timeout": |
252 | return 1 | |
461ea624 | 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)) | |
7c8d37d7 | 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)) | |
461ea624 | 260 | return 0 |
261 | ||
7c8d37d7 | 262 | def notifyTest(name, args): |
344b33ab | 263 | """ |
264 | Sends test notification. | |
265 | ||
266 | This function is bound to notify:send command | |
267 | """ | |
7c8d37d7 | 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 | ||
461ea624 | 281 | ekg.handler_bind('protocol-status', notifyStatus) |
282 | ekg.handler_bind("protocol-message-received", notifyMessage) | |
344b33ab | 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", "^$") | |
461ea624 | 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) | |
7c8d37d7 | 290 | ekg.variable_add("notify:message_notify", "1") |
291 | ekg.variable_add("notify:message_notify_unknown", "1") | |
344b33ab | 292 | ekg.variable_add("notify:show_timestamps", "1") |
461ea624 | 293 | ekg.variable_add("notify:status_timeout", "3500", timeCheck) |
7c8d37d7 | 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) | |
461ea624 | 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 |