From 833ddeb822493f6ab2abd6f193b4c0bc8ea76854 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Tue, 29 Apr 2014 20:37:32 +0200 Subject: Overall design finished --- beinc_server.json | 80 +++++++------ beinc_server.py | 6 +- beinc_weechat.py | 348 ++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 334 insertions(+), 100 deletions(-) diff --git a/beinc_server.json b/beinc_server.json index 0bdcc8a..2824dea 100644 --- a/beinc_server.json +++ b/beinc_server.json @@ -1,42 +1,48 @@ { - "server": { - "general": { - "ssl_module": "pyopenssl", - "ssl_private_key": "key.pem", - "ssl_certificate": "cert.crt" - }, + "server": { + "general": { + "ssl_module": "pyopenssl", + "ssl_private_key": "key.pem", + "ssl_certificate": "cert.crt" + }, - "instances": [ - { - "name": "test", - "password": "changeme", - "osd_system": "pynotify", - "osd_timeout": 5000, - "queue_size": 4 - } - ] - }, + "instances": [ + { + "name": "test", + "password": "changeme", + "osd_system": "pynotify", + "osd_timeout": 5000, + "queue_size": 4 + } + ] + }, - "irc_client": { - "targets": [ - { - "name": "weechat_main", - "target_url": "", - "target_password": "changeme", - "title_template": "", - "message_template": "", - "channel_list": ["RedpillLinpro.#python", - "Exile.#hin", - "RedpillLinpro.#adult", - "Exile.#pichove"], - "nick_list": "", - "channel_messages_policy": 0, - "private_messages_policy": 0, - "notifications_policy": 0 - } - ] - }, - - "config_version": 1 + "irc_client": { + "targets": [ + { + "name": "weechat_main", + "target_url": "", + "target_password": "changeme", + "target_cert_file": "", + "target_timestamp_format": "%H:%M:%S", + "pm_title_template": "", + "pm_message_template": "", + "cm_title_template": "", + "cm_message_template": "", + "nm_title_template": "", + "nm_message_template": "", + "channel_list": ["RedpillLinpro.#python", + "Exile.#hin", + "RedpillLinpro.#adult", + "Exile.#pichove"], + "nick_list": ["Exile.Blackmore"], + "channel_messages_policy": 0, + "private_messages_policy": 0, + "notifications_policy": 0 + } + ] + }, + "config_version": 1 + } diff --git a/beinc_server.py b/beinc_server.py index 300c954..8687da3 100755 --- a/beinc_server.py +++ b/beinc_server.py @@ -206,12 +206,13 @@ class WebNotifyServer(object): try: instance = self.__instances[args[0]] except Exception as e: - sys.stderr.write('Wrong instance or password: {0}\n'.format(e)) + sys.stderr.write('Wrong instance or password\n') + #print('DEBUG: {0}'.format(e)) raise cherrypy.HTTPError('403 Forbidden', 'Wrong instance or password') if not instance.password_match(kwargs.get('password')): - sys.stderr.write('Wrong instance or password: {0}\n'.format(e)) + sys.stderr.write('Wrong instance or password\n') raise cherrypy.HTTPError('403 Forbidden', 'Wrong instance or password') @@ -282,6 +283,7 @@ def main(): except Exception as e: sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, e)) + sys.exit(1) cherrypy.config.update({ 'server.socket_host': args.hostname, diff --git a/beinc_weechat.py b/beinc_weechat.py index 4abf6f1..689f450 100644 --- a/beinc_weechat.py +++ b/beinc_weechat.py @@ -1,14 +1,62 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import datetime +import httplib import json import os +import random import socket +import ssl import sys +import urllib +import urllib2 import weechat enabled = True +global_values = dict() + +# few constants # +BEINC_POLICY_NONE = 0 +BEINC_POLICY_ALL = 1 +BEINC_POLICY_LIST_ONLY = 2 + + + +class ValidHTTPSConnection(httplib.HTTPConnection): + """ + """ + + default_port = httplib.HTTPS_PORT + + def __init__(self, cert_file, *args, **kwargs): + httplib.HTTPConnection.__init__(self, *args, **kwargs) + self.__cert_file = cert_file + + + def connect(self): + sock = socket.create_connection((self.host, self.port), + self.timeout, self.source_address) + if self._tunnel_host: + self.sock = sock + self._tunnel() + self.sock = ssl.wrap_socket(sock, + ca_certs=self.__cert_file, + cert_reqs=ssl.CERT_REQUIRED) + + + +class ValidHTTPSHandler(urllib2.HTTPSHandler): + + def __init__(self, cert_file, *args, **kwargs): + urllib2.HTTPSHandler.__init__(self, *args, **kwargs) + self.__cert_file = cert_file + + def https_open(self, req): + return self.do_open(ValidHTTPSConnection(self.__cert_file), req) + + class WeechatTarget(object): """ @@ -18,80 +66,223 @@ class WeechatTarget(object): """ target_dict: the config-dictionary node that represents this instance """ + self.__name = target_dict.get( + 'name', + ''.join([chr(random.randrange(97, 123)) for x in range(4)])) + self.__url = target_dict.get('target_url') + self.__password = target_dict.get('target_password') + + self.__pm_title_template = target_dict.get('pm_title_template', + '%s @ %S') + self.__pm_message_template = target_dict.get('pm_message_template', + '%m') + + self.__cm_title_template = target_dict.get('cm_title_template', + '%c @ %S') + self.__cm_message_template = target_dict.get('cm_message_template', + '%s -> %m') + + self.__nm_title_template = target_dict.get('nm_title_template', + '%c @ %S') + self.__nm_message_template = target_dict.get('nm_message_template', + '%s -> %m') + + self.__chans = set(target_dict.get('channel_list', list())) + self.__nicks = set(target_dict.get('nick_list', list())) + self.__chan_messages_policy = int(target_dict.get( + 'channel_messages_policy', + BEINC_POLICY_LIST_ONLY)) + self.__priv_messages_policy = int(target_dict.get( + 'private_messages_policy', + BEINC_POLICY_ALL)) + self.__notifications_policy = int(target_dict.get( + 'notifications_policy', + BEINC_POLICY_ALL)) + self.__cert_file = target_dict.get('target_cert_file') + self.__timestamp_format = target_dict.get('target_timestamp_format', + '%H:%M:%S') + + + @property + def name(self): + """ + """ + return self.__name + + + @property + def chans(self): + """ + """ + return self.__chans + + + @property + def nicks(self): + """ + """ + return self.__nicks + + + @property + def channel_messages_policy(self): + """ + """ + return self.__chan_messages_policy + + + @property + def private_messages_policy(self): + """ + """ + return self.__priv_messages_policy + - self.__name = target_dict['name'] - self.__url = target_dict['target_url'] - self.__password = target_dict['target_password'] - self.__title_template = target_dict['title_template'] - self.__message_template = target_dict['message_template'] - self.__failed = False + @property + def notifications_policy(self): + """ + """ + return self.__notifications_policy + + + def __repr__(self): + """ + """ + return 'name: {0}\nurl: {1}\nchannel_list: {2}\nnick_list: {3}'\ + 'channel_messages_policy: {4}\nprivate_messages_policy: {5}'\ + 'notifications_policy: {6}'.format(self.__name, + self.__url, + ', '.join(self.__chans), + ', '.join(self.__nicks), + self.__chan_message_policy, + self.__priv_message_policy, + self.__notifications_policy) + + + def send_private_message_notification(self, message, values): + """ + """ + pass + + def send_channel_message_notification(self, message, values): + """ + """ + pass + + + def send_notify_message_notification(self, message, values): + """ + """ + pass -def bosd_send_xosd_message(message): - for conn in connections: + def __fetch_formatted_str(self, template, values): + """ + """ + replacements = {'%S': values['server'], + '%s': values['source_nick'], + '%c': values['channel'], + '%m': values['message'], + '%t': values['timestamp'], + '%p': 'BEINC', + '%n': values['own_nick']} + for key, value in replacements.items(): + template = template.replace(key, value) + return template + + + def __send_beinc_message(self, data): + """ + the function implements the BEINC "protocol" by generating a simple + HTTP request + """ + try: - #weechat.prnt("", message) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((conn['sbosdd_ip'], conn['sbosdd_port'])) - s.send(message) - s.close() - except: - continue + req = urllib2.Request(self.__url, data) + opener = urllib2.build_opener(ValidHTTPSHandler) + + response = opener.open(req) + res_code = response.code + response.close() + if res_code == 200: + return True + except Exception as e: + weechat.prnt(weechat.current_buffer(), + 'DEBUG: send_beinc_message-ERROR: {0}'.format(e)) + return False - return True -def bosd_command(data, buffer, args): +def beinc_send_message(message): + weechat.prnt(weechat.current_buffer(), 'beinc message: {0}'.format(message)) + + +def beinc_command(data, buffer, args): global enabled if args == 'on': enabled = True - weechat.prnt(weechat.current_buffer(), 'bosd on') + weechat.prnt(weechat.current_buffer(), 'beinc on') elif args == 'off': enabled = False - weechat.prnt(weechat.current_buffer(), 'bosd off') + weechat.prnt(weechat.current_buffer(), 'beinc off') elif args == 'reload': - weechat.prnt(weechat.current_buffer(), '{0} reloaded') + beinc_config_file_str = os.path.join( + weechat.info_get('weechat_dir', ''), + 'beinc.json') + weechat.prnt(weechat.current_buffer(), '{0} reloaded'.format( + beinc_config_file_str)) else: - bosd_send_xosd_message(args) + beinc_send_message(args) return weechat.WEECHAT_RC_OK -def bosd_privmsg_handler(data, signal, signal_data): +def beinc_privmsg_handler(data, signal, signal_data): if not enabled: return weechat.WEECHAT_RC_OK - prvmsg_dict = weechat.info_get_hashtable("irc_message_parse", - { "message": signal_data }) - - - server = signal.split(",")[0] - - # channel = signal_data.split(":")[-1] - # my_nick = weechat.info_get("irc_nick_from_host", signal_data) - - my_nick = weechat.info_get("irc_nick", server) - channel = prvmsg_dict['arguments'].split(":")[0].strip() - nick = prvmsg_dict['nick'] - message = ':'.join(prvmsg_dict['arguments'].split(':')[1:]).strip() - - if (my_nick.lower() in message.lower()) or ('{0}.{1}'.format(server,channel.lower()) in notify_channels): - template_msg = '{0} @ {1} - {2}: {3}'.format(channel, - server, - nick, - message) - bosd_send_xosd_message(template_msg) - - if my_nick.lower() in channel.lower(): - template_msg = '{0} @ {1}: {2}'.format(nick, server, message) - bosd_send_xosd_message(template_msg) - - # weechat.prnt("", - # 'DEBUG: my_nick - {0}, server - {1}, channel - {2}'.format(my_nick, - # server, - # channel)) + prvmsg_dict = weechat.info_get_hashtable('irc_message_parse', + {'message': signal_data }) + + # packing the privmsg handler values + ph_values = dict() + ph_values['server'] = signal.split(',')[0] + ph_values['own_nick'] = weechat.info_get('irc_nick', server) + ph_values['channel'] = prvmsg_dict['arguments'].split(':')[0].strip() + ph_values['source_nick'] = prvmsg_dict['nick'] + ph_values['message'] = ':'.join( + prvmsg_dict['arguments'].split(':')[1:]).strip() + ph_values['timestamp'] = datetime.datetime.now().strftime( + self.__timestamp_format) + + if ph_values['channel'] == ph_values['own_nick']: + # priv messages are handled here + if not global_values['global_channel_messages_policy']: + return weechat.WEECHAT_RC_OK + + for target in target_list: + if target.private_messages_policy == 1 or ( + target.private_messages_policy == 2 \ + and '{0}.{1}'.format( + ph_values['server'], + ph_values['source_nick'].lower()) in target.nicks): + weechat.prnt(weechat.current_buffer(), + 'DEBUG: priv message - {0}'.format( + ph_values['message'])) + + elif privmsg_handler_values['own_nick'].lower() in ph_values['message'].lower(): + # notify messages are handled here + weechat.prnt(weechat.current_buffer(), + 'DEBUG: notify message - {0}'.format(ph_values['message'])) + if not global_values['global_notifications_policy']: + return weechat.WEECHAT_RC_OK + + elif global_values['global_channel_messages_policy']: + # chan messages are handled here + weechat.prnt(weechat.current_buffer(), + 'DEBUG: chan message - {0}'.format(ph_values['message'])) return weechat.WEECHAT_RC_OK @@ -100,13 +291,20 @@ def beinc_init(): global enabled global target_list + global global_values + global_values['global_chans'] = set() + global_values['global_nicks'] = set() custom_error = '' + global_values['global_channel_messages_policy'] = False + global_values['global_private_messages_policy'] = False + global_values['global_notifications_policy'] = False + try: - beinc_config_file_str = os.path.join(weechat.info_get('weechat_dir', - ''), - 'beinc.json') + beinc_config_file_str = os.path.join( + weechat.info_get('weechat_dir', ''), + 'beinc.json') weechat.prnt('', 'Parsing {0}...'.format(beinc_config_file_str)) custom_error = 'load error' @@ -118,8 +316,22 @@ def beinc_init(): custom_error = 'target parse error' for target in config_dict['irc_client']['targets']: - new_target = WeechatTarget(target) + try: + new_target = WeechatTarget(target) + except Exception as e: + weechat.prnt('', 'Unable to add target: {0}'.format(e)) + continue + global_values['global_chans'].update(new_target.chans) + global_values['global_nicks'].update(new_target.nicks) + if new_target.channel_messages_policy: + global_values['global_channel_messages_policy'] = True + if new_target.private_messages_policy: + global_values['global_private_messages_policy'] = True + if new_target.notifications_policy: + global_values['global_notifications_policy'] = True + target_list.append(new_target) + weechat.prnt('', 'BEINC target {0} added'.format(new_target.name)) weechat.prnt('', 'Done!!!') @@ -135,10 +347,24 @@ def beinc_init(): return weechat.WEECHAT_RC_OK -weechat.register('beinc_weechat', 'Simeon Simeonov', '1.0', 'GPL3', 'Blackmore\'s Extended IRC Notification Collection (Weechat Client)', '', '') -weechat.hook_command('beinc', 'beinc on off toggle', '', 'description...', 'None', 'beinc_command', '') -weechat.hook_signal('*,irc_in2_privmsg', 'beinc_privmsg_handler', '') - -beinc_init() -weechat.prnt('', 'beinc initiated!') +weechat.register('beinc_weechat', + 'Simeon Simeonov', + '1.0', + 'GPL3', + 'Blackmore\'s Extended IRC Notification Collection (Weechat Client)', + '', + '') +version = weechat.info_get('version_number', '') or 0 +if int(version) < 0x00040000: + weechat.prnt('', 'WeeChat version >= 0.4.0 is required to run beinc') +else: + weechat.hook_command('beinc', + 'beinc on off toggle', '', + 'description...', + 'None', + 'beinc_command', + '') + weechat.hook_signal('*,irc_in2_privmsg', 'beinc_privmsg_handler', '') + beinc_init() + weechat.prnt('', 'beinc initiated!') -- cgit v1.3