summaryrefslogtreecommitdiff
path: root/beinc_weechat.py
diff options
context:
space:
mode:
authorSimeon Simeonov2014-05-07 22:28:48 +0200
committerSimeon Simeonov2014-05-07 22:28:48 +0200
commit60f47b04174d016cc86c6e06fdaf0794c3d14216 (patch)
treeb8137a37b8e4b81202db5943b509652c7f9564b5 /beinc_weechat.py
parent833ddeb822493f6ab2abd6f193b4c0bc8ea76854 (diff)
Git server / generic client and weechat client completed
Diffstat (limited to 'beinc_weechat.py')
-rw-r--r--beinc_weechat.py327
1 files changed, 258 insertions, 69 deletions
diff --git a/beinc_weechat.py b/beinc_weechat.py
index 689f450..8b4392f 100644
--- a/beinc_weechat.py
+++ b/beinc_weechat.py
@@ -1,6 +1,23 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2# -*- coding: utf-8 -*- 2# -*- coding: utf-8 -*-
3 3
4# Blackmore's Enhanced IRC-Notification Collection (BEINC) v1.0
5# Copyright (C) 2013-2014 Simeon Simeonov
6
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
4import datetime 21import datetime
5import httplib 22import httplib
6import json 23import json
@@ -14,6 +31,12 @@ import urllib2
14 31
15import weechat 32import weechat
16 33
34
35__author__ = 'Simeon Simeonov'
36__version__ = '1.0'
37__license__ = 'GPL3'
38
39
17enabled = True 40enabled = True
18global_values = dict() 41global_values = dict()
19 42
@@ -26,14 +49,13 @@ BEINC_POLICY_LIST_ONLY = 2
26 49
27class ValidHTTPSConnection(httplib.HTTPConnection): 50class ValidHTTPSConnection(httplib.HTTPConnection):
28 """ 51 """
52 Implements a simple CERT verification functionality
29 """ 53 """
30 54
31 default_port = httplib.HTTPS_PORT 55 default_port = httplib.HTTPS_PORT
32 56
33 def __init__(self, cert_file, *args, **kwargs): 57 def __init__(self, *args, **kwargs):
34 httplib.HTTPConnection.__init__(self, *args, **kwargs) 58 httplib.HTTPConnection.__init__(self, *args, **kwargs)
35 self.__cert_file = cert_file
36
37 59
38 def connect(self): 60 def connect(self):
39 sock = socket.create_connection((self.host, self.port), 61 sock = socket.create_connection((self.host, self.port),
@@ -42,19 +64,18 @@ class ValidHTTPSConnection(httplib.HTTPConnection):
42 self.sock = sock 64 self.sock = sock
43 self._tunnel() 65 self._tunnel()
44 self.sock = ssl.wrap_socket(sock, 66 self.sock = ssl.wrap_socket(sock,
45 ca_certs=self.__cert_file, 67 ca_certs=global_beinc_cert_file,
46 cert_reqs=ssl.CERT_REQUIRED) 68 cert_reqs=ssl.CERT_REQUIRED)
47 69
48 70
49 71
50class ValidHTTPSHandler(urllib2.HTTPSHandler): 72class ValidHTTPSHandler(urllib2.HTTPSHandler):
51 73 """
52 def __init__(self, cert_file, *args, **kwargs): 74 Implements a simple CERT verification functionality
53 urllib2.HTTPSHandler.__init__(self, *args, **kwargs) 75 """
54 self.__cert_file = cert_file
55 76
56 def https_open(self, req): 77 def https_open(self, req):
57 return self.do_open(ValidHTTPSConnection(self.__cert_file), req) 78 return self.do_open(ValidHTTPSConnection, req)
58 79
59 80
60 81
@@ -101,6 +122,8 @@ class WeechatTarget(object):
101 self.__cert_file = target_dict.get('target_cert_file') 122 self.__cert_file = target_dict.get('target_cert_file')
102 self.__timestamp_format = target_dict.get('target_timestamp_format', 123 self.__timestamp_format = target_dict.get('target_timestamp_format',
103 '%H:%M:%S') 124 '%H:%M:%S')
125 self.__debug = bool(target_dict.get('debug', False))
126 self.__enabled = bool(target_dict.get('enabled', True))
104 127
105 128
106 @property 129 @property
@@ -144,48 +167,117 @@ class WeechatTarget(object):
144 """ 167 """
145 return self.__notifications_policy 168 return self.__notifications_policy
146 169
147 170
171 @property
172 def enabled(self):
173 """
174 """
175 return self.__enabled
176
177 @enabled.setter
178 def enabled(self, value):
179 """
180 """
181 self.__enabled = value
182
183
148 def __repr__(self): 184 def __repr__(self):
149 """ 185 """
150 """ 186 """
151 187 return 'name: {0}\nurl: {1}\nchannel_list: {2}\nnick_list: {3}\n'\
152 return 'name: {0}\nurl: {1}\nchannel_list: {2}\nnick_list: {3}'\ 188 'channel_messages_policy: {4}\nprivate_messages_policy: {5}\n'\
153 'channel_messages_policy: {4}\nprivate_messages_policy: {5}'\ 189 'notifications_policy: {6}\nenabled: {7}\n\n'.format(
154 'notifications_policy: {6}'.format(self.__name, 190 self.__name,
155 self.__url, 191 self.__url,
156 ', '.join(self.__chans), 192 ', '.join(self.__chans),
157 ', '.join(self.__nicks), 193 ', '.join(self.__nicks),
158 self.__chan_message_policy, 194 self.__chan_messages_policy,
159 self.__priv_message_policy, 195 self.__priv_messages_policy,
160 self.__notifications_policy) 196 self.__notifications_policy,
197 'yes' if self.__enabled else 'no')
161 198
162 199
163 def send_private_message_notification(self, message, values): 200 def send_private_message_notification(self, values):
164 """ 201 """
165 """ 202 """
166 pass 203 try:
204 title_str = self.__fetch_formatted_str(self.__pm_title_template,
205 values)
206 message_str = self.__fetch_formatted_str(self.__pm_message_template,
207 values)
208 post_values = {'title': title_str,
209 'message': message_str,
210 'password': self.__password}
211 data = urllib.urlencode(post_values)
212 if self.__send_beinc_message(data) and self.__debug:
213 beinc_prnt(
214 'BEINC DEBUG: send_private_message_notification-ERROR '
215 'for "{0}": __send_beinc_message -> False'.format(
216 self.__name))
217 except Exception as e:
218 if self.__debug:
219 beinc_prnt(
220 'BEINC DEBUG: send_private_message_notification-ERROR '
221 'for "{0}": {1}'.format(self.__name, e))
167 222
168 223
169 def send_channel_message_notification(self, message, values): 224 def send_channel_message_notification(self, values):
170 """ 225 """
171 """ 226 """
172 pass 227 try:
228 title_str = self.__fetch_formatted_str(self.__cm_title_template,
229 values)
230 message_str = self.__fetch_formatted_str(self.__cm_message_template,
231 values)
232 post_values = {'title': title_str,
233 'message': message_str,
234 'password': self.__password}
235 data = urllib.urlencode(post_values)
236 if self.__send_beinc_message(data) and self.__debug:
237 beinc_prnt(
238 'BEINC DEBUG: send_channel_message_notification-ERROR '
239 'for "{0}": __send_beinc_message -> False'.format(
240 self.__name))
241 except Exception as e:
242 if self.__debug:
243 beinc_prnt(
244 'BEINC DEBUG: send_channel_message_notification-ERROR '
245 'for "{0}": {1}'.format(self.__name, e))
173 246
174 247
175 def send_notify_message_notification(self, message, values): 248 def send_notify_message_notification(self, values):
176 """ 249 """
177 """ 250 """
178 pass 251 try:
252 title_str = self.__fetch_formatted_str(self.__nm_title_template,
253 values)
254 message_str = self.__fetch_formatted_str(self.__nm_message_template,
255 values)
256 post_values = {'title': title_str,
257 'message': message_str,
258 'password': self.__password}
259 data = urllib.urlencode(post_values)
260 if self.__send_beinc_message(data) and self.__debug:
261 beinc_prnt(
262 'BEINC DEBUG: send_notify_message_notification-ERROR '
263 'for "{0}": __send_beinc_message -> False'.format(
264 self.__name))
265 except Exception as e:
266 if self.__debug:
267 beinc_prnt(
268 'BEINC DEBUG: send_notify_message_notification-ERROR '
269 'for "{0}": {1}'.format(self.__name, e))
179 270
180 271
181 def __fetch_formatted_str(self, template, values): 272 def __fetch_formatted_str(self, template, values):
182 """ 273 """
183 """ 274 """
275 timestamp = datetime.datetime.now().strftime(self.__timestamp_format)
184 replacements = {'%S': values['server'], 276 replacements = {'%S': values['server'],
185 '%s': values['source_nick'], 277 '%s': values['source_nick'],
186 '%c': values['channel'], 278 '%c': values['channel'],
187 '%m': values['message'], 279 '%m': values['message'],
188 '%t': values['timestamp'], 280 '%t': timestamp,
189 '%p': 'BEINC', 281 '%p': 'BEINC',
190 '%n': values['own_nick']} 282 '%n': values['own_nick']}
191 for key, value in replacements.items(): 283 for key, value in replacements.items():
@@ -201,40 +293,100 @@ class WeechatTarget(object):
201 293
202 try: 294 try:
203 req = urllib2.Request(self.__url, data) 295 req = urllib2.Request(self.__url, data)
204 opener = urllib2.build_opener(ValidHTTPSHandler) 296
205 297 if self.__cert_file:
206 response = opener.open(req) 298 opener = urllib2.build_opener(ValidHTTPSHandler)
299 response = opener.open(req)
300 else:
301 response = urllib2.urlopen(req)
207 res_code = response.code 302 res_code = response.code
208 response.close() 303 response.close()
209 if res_code == 200: 304 if res_code == 200:
210 return True 305 return True
211 except Exception as e: 306 except urllib2.HTTPError as e:
212 weechat.prnt(weechat.current_buffer(), 307 if self.__debug:
213 'DEBUG: send_beinc_message-ERROR: {0}'.format(e)) 308 beinc_prnt(
309 'BEINC DEBUG: send_beinc_message-ERROR for "{0}": {1} ->'
310 ' ({2} - {3})'.format(self.__name, e.url, e.code, e.reason))
311 # all other exception should be handled by the caller
214 return False 312 return False
215 313
216 314
217 315
218def beinc_send_message(message): 316def beinc_prnt(message_str):
219 weechat.prnt(weechat.current_buffer(), 'beinc message: {0}'.format(message)) 317 """
318 wrapper around weechat.prnt
319 """
320 if global_values['use_current_buffer']:
321 weechat.prnt(weechat.current_buffer(), message_str)
322 else:
323 weechat.prnt('', message_str)
324
220 325
326def beinc_cmd_target_handler(cmd_tokens):
327 """
328 handles: '/beinc target' command actions
329 """
330 if not cmd_tokens or cmd_tokens[0] not in ['list', 'enable', 'disable']:
331 beinc_prnt('beinc target [ list | enable <name> | disable <name> ]')
332 return weechat.WEECHAT_RC_OK
221 333
222def beinc_command(data, buffer, args): 334 if cmd_tokens[0] == 'list':
335 beinc_prnt('--- Targets ---')
336 for target in target_list:
337 beinc_prnt(str(target))
338 beinc_prnt('---------------')
339 elif cmd_tokens[0] == 'enable':
340 if not cmd_tokens[1:]:
341 beinc_prnt('missing a name-argument')
342 return weechat.WEECHAT_RC_OK
343 name = ' '.join(cmd_tokens[1:])
344 for target in target_list:
345 if target.name == name:
346 target.enabled = True
347 beinc_prnt('target "{0}" enabled'.format(name))
348 break
349 else:
350 beinc_prnt('no matching target for "{0}"'.format(name))
351 elif cmd_tokens[0] == 'disable':
352 if not cmd_tokens[1:]:
353 beinc_prnt('missing a name-argument')
354 return weechat.WEECHAT_RC_OK
355 name = ' '.join(cmd_tokens[1:])
356 for target in target_list:
357 if target.name == name:
358 target.enabled = False
359 beinc_prnt('target "{0}" disabled'.format(name))
360 break
361 else:
362 beinc_prnt('no matching target for "{0}"'.format(name))
363
364 return weechat.WEECHAT_RC_OK
365
366
367def beinc_command(data, buffer_obj, args):
223 global enabled 368 global enabled
369 cmd_tokens = args.split()
370
371 if not cmd_tokens:
372 return weechat.WEECHAT_RC_OK
373
224 if args == 'on': 374 if args == 'on':
225 enabled = True 375 enabled = True
226 weechat.prnt(weechat.current_buffer(), 'beinc on') 376 beinc_prnt('BEINC on')
227 elif args == 'off': 377 elif args == 'off':
228 enabled = False 378 enabled = False
229 weechat.prnt(weechat.current_buffer(), 'beinc off') 379 beinc_prnt('BEINC off')
230 elif args == 'reload': 380 elif args == 'reload':
231 beinc_config_file_str = os.path.join( 381 beinc_prnt('Reloading BEINC...')
232 weechat.info_get('weechat_dir', ''), 382 beinc_init()
233 'beinc.json') 383 elif cmd_tokens[0] == 'target':
234 weechat.prnt(weechat.current_buffer(), '{0} reloaded'.format( 384 return beinc_cmd_target_handler(cmd_tokens[1:])
235 beinc_config_file_str))
236 else: 385 else:
237 beinc_send_message(args) 386 beinc_prnt('data: {0}, cmd_tokens: {1}, args: {2}'.format(
387 str(data),
388 str(cmd_tokens),
389 str(args)))
238 390
239 return weechat.WEECHAT_RC_OK 391 return weechat.WEECHAT_RC_OK
240 392
@@ -249,40 +401,74 @@ def beinc_privmsg_handler(data, signal, signal_data):
249 # packing the privmsg handler values 401 # packing the privmsg handler values
250 ph_values = dict() 402 ph_values = dict()
251 ph_values['server'] = signal.split(',')[0] 403 ph_values['server'] = signal.split(',')[0]
252 ph_values['own_nick'] = weechat.info_get('irc_nick', server) 404 ph_values['own_nick'] = weechat.info_get('irc_nick', ph_values['server'])
253 ph_values['channel'] = prvmsg_dict['arguments'].split(':')[0].strip() 405 ph_values['channel'] = prvmsg_dict['arguments'].split(':')[0].strip()
254 ph_values['source_nick'] = prvmsg_dict['nick'] 406 ph_values['source_nick'] = prvmsg_dict['nick']
255 ph_values['message'] = ':'.join( 407 ph_values['message'] = ':'.join(
256 prvmsg_dict['arguments'].split(':')[1:]).strip() 408 prvmsg_dict['arguments'].split(':')[1:]).strip()
257 ph_values['timestamp'] = datetime.datetime.now().strftime(
258 self.__timestamp_format)
259 409
260 if ph_values['channel'] == ph_values['own_nick']: 410 if ph_values['channel'] == ph_values['own_nick']:
261 # priv messages are handled here 411 # priv messages are handled here
262 if not global_values['global_channel_messages_policy']: 412 if not global_values['global_private_messages_policy']:
413 return weechat.WEECHAT_RC_OK
414
415 if global_values['global_private_messages_policy'] == BEINC_POLICY_LIST_ONLY \
416 and '{0}.{1}'.format(
417 ph_values['server'],
418 ph_values['source_nick'].lower()) not in global_values['global_nicks']:
263 return weechat.WEECHAT_RC_OK 419 return weechat.WEECHAT_RC_OK
264 420
265 for target in target_list: 421 for target in target_list:
266 if target.private_messages_policy == 1 or ( 422 if not target.enabled:
267 target.private_messages_policy == 2 \ 423 continue
424 if target.private_messages_policy == BEINC_POLICY_ALL or (
425 target.private_messages_policy == BEINC_POLICY_LIST_ONLY \
268 and '{0}.{1}'.format( 426 and '{0}.{1}'.format(
269 ph_values['server'], 427 ph_values['server'],
270 ph_values['source_nick'].lower()) in target.nicks): 428 ph_values['source_nick'].lower()) in target.nicks):
271 weechat.prnt(weechat.current_buffer(), 429 target.send_private_message_notification(ph_values)
272 'DEBUG: priv message - {0}'.format(
273 ph_values['message']))
274 430
275 elif privmsg_handler_values['own_nick'].lower() in ph_values['message'].lower(): 431 elif ph_values['own_nick'].lower() in ph_values['message'].lower():
276 # notify messages are handled here 432 # notify messages are handled here
277 weechat.prnt(weechat.current_buffer(),
278 'DEBUG: notify message - {0}'.format(ph_values['message']))
279 if not global_values['global_notifications_policy']: 433 if not global_values['global_notifications_policy']:
280 return weechat.WEECHAT_RC_OK 434 return weechat.WEECHAT_RC_OK
281 435
436 if global_values['global_notifications_policy'] == BEINC_POLICY_LIST_ONLY \
437 and '{0}.{1}'.format(
438 ph_values['server'],
439 ph_values['channel'].lower()) not in global_values['global_chans']:
440 return weechat.WEECHAT_RC_OK
441
442 for target in target_list:
443 if not target.enabled:
444 continue
445 if target.notifications_policy == BEINC_POLICY_ALL or (
446 target.notifications_policy == BEINC_POLICY_LIST_ONLY \
447 and '{0}.{1}'.format(
448 ph_values['server'],
449 ph_values['channel'].lower()) in target.chans):
450 target.send_notify_message_notification(ph_values)
451
282 elif global_values['global_channel_messages_policy']: 452 elif global_values['global_channel_messages_policy']:
283 # chan messages are handled here 453 # chan messages are handled here
284 weechat.prnt(weechat.current_buffer(), 454 if not global_values['global_notifications_policy']:
285 'DEBUG: chan message - {0}'.format(ph_values['message'])) 455 return weechat.WEECHAT_RC_OK
456
457 if global_values['global_channel_messages_policy'] == BEINC_POLICY_LIST_ONLY \
458 and '{0}.{1}'.format(
459 ph_values['server'],
460 ph_values['channel'].lower()) not in global_values['global_chans']:
461 return weechat.WEECHAT_RC_OK
462
463 for target in target_list:
464 if not target.enabled:
465 continue
466 if target.channel_messages_policy == BEINC_POLICY_ALL or (
467 target.channel_messages_policy == BEINC_POLICY_LIST_ONLY \
468 and '{0}.{1}'.format(
469 ph_values['server'],
470 ph_values['channel'].lower()) in target.chans):
471 target.send_channel_message_notification(ph_values)
286 472
287 return weechat.WEECHAT_RC_OK 473 return weechat.WEECHAT_RC_OK
288 474
@@ -293,33 +479,36 @@ def beinc_init():
293 global target_list 479 global target_list
294 global global_values 480 global global_values
295 481
482 # global chans/nicks sets are used to speed up the filtering
483 global_values = dict()
296 global_values['global_chans'] = set() 484 global_values['global_chans'] = set()
297 global_values['global_nicks'] = set() 485 global_values['global_nicks'] = set()
298 custom_error = '' 486 target_list = list()
299 487
488 custom_error = ''
300 global_values['global_channel_messages_policy'] = False 489 global_values['global_channel_messages_policy'] = False
301 global_values['global_private_messages_policy'] = False 490 global_values['global_private_messages_policy'] = False
302 global_values['global_notifications_policy'] = False 491 global_values['global_notifications_policy'] = False
303 492 global_values['use_current_buffer'] = False
493
304 try: 494 try:
305 beinc_config_file_str = os.path.join( 495 beinc_config_file_str = os.path.join(
306 weechat.info_get('weechat_dir', ''), 496 weechat.info_get('weechat_dir', ''),
307 'beinc.json') 497 'beinc.json')
308 weechat.prnt('', 'Parsing {0}...'.format(beinc_config_file_str)) 498 beinc_prnt('Parsing {0}...'.format(beinc_config_file_str))
309 499
310 custom_error = 'load error' 500 custom_error = 'load error'
311 with open(beinc_config_file_str, 'r') as fp: 501 with open(beinc_config_file_str, 'r') as fp:
312 config_dict = json.load(fp) 502 config_dict = json.load(fp)
313 503
314 # clear the target-list
315 target_list = []
316
317 custom_error = 'target parse error' 504 custom_error = 'target parse error'
505 global_values['use_current_buffer'] = bool(config_dict['irc_client'].get(
506 'use_current_buffer', False))
318 for target in config_dict['irc_client']['targets']: 507 for target in config_dict['irc_client']['targets']:
319 try: 508 try:
320 new_target = WeechatTarget(target) 509 new_target = WeechatTarget(target)
321 except Exception as e: 510 except Exception as e:
322 weechat.prnt('', 'Unable to add target: {0}'.format(e)) 511 beinc_prnt('Unable to add target: {0}'.format(e))
323 continue 512 continue
324 global_values['global_chans'].update(new_target.chans) 513 global_values['global_chans'].update(new_target.chans)
325 global_values['global_nicks'].update(new_target.nicks) 514 global_values['global_nicks'].update(new_target.nicks)
@@ -331,12 +520,12 @@ def beinc_init():
331 global_values['global_notifications_policy'] = True 520 global_values['global_notifications_policy'] = True
332 521
333 target_list.append(new_target) 522 target_list.append(new_target)
334 weechat.prnt('', 'BEINC target {0} added'.format(new_target.name)) 523 beinc_prnt('BEINC target "{0}" added'.format(new_target.name))
335 524
336 weechat.prnt('', 'Done!!!') 525 beinc_prnt('Done!')
337 526
338 except Exception as e: 527 except Exception as e:
339 weechat.prnt('', 'ERROR: unable to parse {0}: {1} - {2}'.format( 528 beinc_prnt('ERROR: unable to parse {0}: {1} - {2}'.format(
340 beinc_config_file_str, custom_error, e)) 529 beinc_config_file_str, custom_error, e))
341 enabled = False 530 enabled = False
342 531