diff options
| author | Simeon Simeonov | 2014-05-07 22:28:48 +0200 |
|---|---|---|
| committer | Simeon Simeonov | 2014-05-07 22:28:48 +0200 |
| commit | 60f47b04174d016cc86c6e06fdaf0794c3d14216 (patch) | |
| tree | b8137a37b8e4b81202db5943b509652c7f9564b5 | |
| parent | 833ddeb822493f6ab2abd6f193b4c0bc8ea76854 (diff) | |
Git server / generic client and weechat client completed
| -rwxr-xr-x | beinc_generic_client.py | 158 | ||||
| -rw-r--r-- | beinc_server.json | 25 | ||||
| -rwxr-xr-x | beinc_server.py | 148 | ||||
| -rw-r--r-- | beinc_weechat.py | 327 | ||||
| -rw-r--r-- | bosd.py | 101 | ||||
| -rw-r--r-- | bpynotify_orm.py | 32 | ||||
| -rwxr-xr-x | socket_server.py | 77 | ||||
| -rwxr-xr-x | web_server.py | 47 | ||||
| -rwxr-xr-x | weechat_notify.py | 49 | ||||
| -rwxr-xr-x | weechat_notify_server.py | 116 |
10 files changed, 507 insertions, 573 deletions
diff --git a/beinc_generic_client.py b/beinc_generic_client.py new file mode 100755 index 0000000..6cffc84 --- /dev/null +++ b/beinc_generic_client.py | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 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 | |||
| 21 | import argparse | ||
| 22 | import errno | ||
| 23 | import getpass | ||
| 24 | import httplib | ||
| 25 | import socket | ||
| 26 | import ssl | ||
| 27 | import sys | ||
| 28 | import urllib | ||
| 29 | import urllib2 | ||
| 30 | |||
| 31 | |||
| 32 | __author__ = 'Simeon Simeonov' | ||
| 33 | __version__ = '1.0' | ||
| 34 | __license__ = 'GPL3' | ||
| 35 | |||
| 36 | |||
| 37 | class ValidHTTPSConnection(httplib.HTTPConnection): | ||
| 38 | """ | ||
| 39 | Implements a simple CERT verification functionality | ||
| 40 | """ | ||
| 41 | |||
| 42 | default_port = httplib.HTTPS_PORT | ||
| 43 | |||
| 44 | def __init__(self, *args, **kwargs): | ||
| 45 | httplib.HTTPConnection.__init__(self, *args, **kwargs) | ||
| 46 | |||
| 47 | def connect(self): | ||
| 48 | sock = socket.create_connection((self.host, self.port), | ||
| 49 | self.timeout, self.source_address) | ||
| 50 | if self._tunnel_host: | ||
| 51 | self.sock = sock | ||
| 52 | self._tunnel() | ||
| 53 | self.sock = ssl.wrap_socket(sock, | ||
| 54 | ca_certs=global_beinc_cert_file, | ||
| 55 | cert_reqs=ssl.CERT_REQUIRED) | ||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | class ValidHTTPSHandler(urllib2.HTTPSHandler): | ||
| 60 | """ | ||
| 61 | Implements a simple CERT verification functionality | ||
| 62 | """ | ||
| 63 | |||
| 64 | def https_open(self, req): | ||
| 65 | return self.do_open(ValidHTTPSConnection, req) | ||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | def action_push(args): | ||
| 70 | """ | ||
| 71 | """ | ||
| 72 | try: | ||
| 73 | post_values = {'title': args.title, | ||
| 74 | 'message': args.message, | ||
| 75 | 'password': args.password} | ||
| 76 | data = urllib.urlencode(post_values) | ||
| 77 | req = urllib2.Request(args.url, data) | ||
| 78 | if args.cert: # check for cert validity | ||
| 79 | global global_beinc_cert_file # ugly hack | ||
| 80 | global_beinc_cert_file = args.cert | ||
| 81 | opener = urllib2.build_opener(ValidHTTPSHandler) | ||
| 82 | response = opener.open(req) | ||
| 83 | else: # ... or don't | ||
| 84 | response = urllib2.urlopen(req) | ||
| 85 | res_code = response.code | ||
| 86 | if res_code == 200: | ||
| 87 | print('Server responded: OK') | ||
| 88 | else: | ||
| 89 | print('Server responded: {0}'.format(res_code)) | ||
| 90 | print('Body:\n{0}'.format(response.read())) | ||
| 91 | response.close() | ||
| 92 | except urllib2.HTTPError as e: | ||
| 93 | sys.stderr.write('BEINC-server error ({0} - {1})\n'.format(e.code, e.reason)) | ||
| 94 | except Exception as e: | ||
| 95 | sys.stderr.write('BEINC generic client error: {0}\n'.format(e)) | ||
| 96 | sys.exit(errno.EPERM) | ||
| 97 | |||
| 98 | |||
| 99 | def main(): | ||
| 100 | |||
| 101 | parser = argparse.ArgumentParser( | ||
| 102 | description='The following options are available') | ||
| 103 | |||
| 104 | parser.add_argument('url', | ||
| 105 | metavar='URL', | ||
| 106 | type=str, | ||
| 107 | #dest='url', | ||
| 108 | #required=True, | ||
| 109 | help='Destination URL') | ||
| 110 | |||
| 111 | parser.add_argument('-c', '--cert-file', | ||
| 112 | metavar='FILE', | ||
| 113 | type=str, | ||
| 114 | dest='cert', | ||
| 115 | default='', | ||
| 116 | help='BEINC CA-cert to check the server-cert against') | ||
| 117 | |||
| 118 | parser.add_argument('-m', '--message', | ||
| 119 | metavar='MESSAGE', | ||
| 120 | type=str, | ||
| 121 | dest='message', | ||
| 122 | default='BEINC message', | ||
| 123 | help='BEINC message') | ||
| 124 | |||
| 125 | parser.add_argument('-p', '--password', | ||
| 126 | metavar='PASSWORD', | ||
| 127 | type=str, | ||
| 128 | dest='password', | ||
| 129 | default='', | ||
| 130 | help='Password') | ||
| 131 | |||
| 132 | parser.add_argument('-t', '--title', | ||
| 133 | metavar='TITLE', | ||
| 134 | type=str, | ||
| 135 | dest='title', | ||
| 136 | default='BEINC title', | ||
| 137 | help='BEINC title') | ||
| 138 | |||
| 139 | parser.add_argument('-v', '--version', | ||
| 140 | action='version', | ||
| 141 | version='%(prog)s {0}'.format(__version__), | ||
| 142 | help='display program-version and exit') | ||
| 143 | |||
| 144 | args = parser.parse_args() | ||
| 145 | |||
| 146 | if not args.password: | ||
| 147 | try: | ||
| 148 | args.password = getpass.getpass() | ||
| 149 | except Exception as e: | ||
| 150 | sys.stderr.write('Prompt terminated\n') | ||
| 151 | sys.exit(errno.EACCES) | ||
| 152 | |||
| 153 | action_push(args) | ||
| 154 | sys.exit(0) | ||
| 155 | |||
| 156 | |||
| 157 | if __name__ == '__main__': | ||
| 158 | main() | ||
diff --git a/beinc_server.json b/beinc_server.json index 2824dea..46674ed 100644 --- a/beinc_server.json +++ b/beinc_server.json | |||
| @@ -18,27 +18,30 @@ | |||
| 18 | }, | 18 | }, |
| 19 | 19 | ||
| 20 | "irc_client": { | 20 | "irc_client": { |
| 21 | "use_current_buffer": 0, | ||
| 21 | "targets": [ | 22 | "targets": [ |
| 22 | { | 23 | { |
| 23 | "name": "weechat_main", | 24 | "name": "weechat_main", |
| 24 | "target_url": "", | 25 | "target_url": "https://10.0.0.2:9898/push/secondtest", |
| 25 | "target_password": "changeme", | 26 | "target_password": "changeme", |
| 26 | "target_cert_file": "", | 27 | "target_cert_file": "", |
| 27 | "target_timestamp_format": "%H:%M:%S", | 28 | "target_timestamp_format": "%H:%M:%S", |
| 28 | "pm_title_template": "", | 29 | "pm_title_template": "%s @ %S", |
| 29 | "pm_message_template": "", | 30 | "pm_message_template": "%m", |
| 30 | "cm_title_template": "", | 31 | "cm_title_template": "%c @ %S", |
| 31 | "cm_message_template": "", | 32 | "cm_message_template": "%s -> %m", |
| 32 | "nm_title_template": "", | 33 | "nm_title_template": "%c @ %S", |
| 33 | "nm_message_template": "", | 34 | "nm_message_template": "%s -> %m", |
| 34 | "channel_list": ["RedpillLinpro.#python", | 35 | "channel_list": ["RedpillLinpro.#python", |
| 35 | "Exile.#hin", | 36 | "Exile.#test", |
| 36 | "RedpillLinpro.#adult", | 37 | "RedpillLinpro.#adult", |
| 37 | "Exile.#pichove"], | 38 | "Exile.#pichove"], |
| 38 | "nick_list": ["Exile.Blackmore"], | 39 | "nick_list": ["Exile.Blackmore"], |
| 39 | "channel_messages_policy": 0, | 40 | "channel_messages_policy": 2, |
| 40 | "private_messages_policy": 0, | 41 | "private_messages_policy": 1, |
| 41 | "notifications_policy": 0 | 42 | "notifications_policy": 1, |
| 43 | "debug": 1, | ||
| 44 | "enabled": 1 | ||
| 42 | } | 45 | } |
| 43 | ] | 46 | ] |
| 44 | }, | 47 | }, |
diff --git a/beinc_server.py b/beinc_server.py index 8687da3..d4dd3c3 100755 --- a/beinc_server.py +++ b/beinc_server.py | |||
| @@ -1,18 +1,42 @@ | |||
| 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 | |||
| 4 | import argparse | 21 | import argparse |
| 22 | import errno | ||
| 5 | import getpass | 23 | import getpass |
| 6 | import json | 24 | import json |
| 7 | import os | 25 | import os |
| 26 | import random | ||
| 8 | import sys | 27 | import sys |
| 9 | 28 | ||
| 10 | import cherrypy | 29 | import cherrypy |
| 11 | 30 | ||
| 31 | try: | ||
| 32 | import pynotify | ||
| 33 | except ImportError as e: | ||
| 34 | pynotify = None | ||
| 35 | |||
| 12 | 36 | ||
| 13 | __author__ = 'Simeon Simeonov' | 37 | __author__ = 'Simeon Simeonov' |
| 14 | __version__ = '1.0-beta' | 38 | __version__ = '1.0' |
| 15 | __license__ = "GPL3" | 39 | __license__ = 'GPL3' |
| 16 | 40 | ||
| 17 | 41 | ||
| 18 | BEINC_OSD_TYPE_NONE = 0 | 42 | BEINC_OSD_TYPE_NONE = 0 |
| @@ -33,40 +57,35 @@ class BEINCInstance(object): | |||
| 33 | self.__osd_type = BEINC_OSD_TYPE_NONE | 57 | self.__osd_type = BEINC_OSD_TYPE_NONE |
| 34 | self.__osd_notification = None | 58 | self.__osd_notification = None |
| 35 | 59 | ||
| 36 | try: | 60 | self.__name = instance_dict.get('name') |
| 37 | self.__name = instance_dict['name'] | 61 | self.__password = instance_dict.get('password', '') |
| 38 | self.__password = instance_dict['password'] | 62 | self.__queue_size = int(instance_dict.get('queue_size', 3)) |
| 39 | self.__queue_size = int(instance_dict['queue_size']) | ||
| 40 | |||
| 41 | except Exception as e: | ||
| 42 | sys.stderr.write( | ||
| 43 | 'Instance processing error {0}:\n{1}\n'.format(self.__name, | ||
| 44 | e)) | ||
| 45 | sys.exit(1) | ||
| 46 | 63 | ||
| 47 | if instance_dict['osd_system'].lower() == 'pynotify': | 64 | if instance_dict['osd_system'].lower() == 'pynotify': |
| 65 | self.__queue_size = 0 # disable queueing | ||
| 48 | if not pynotify: | 66 | if not pynotify: |
| 49 | sys.stderr.write( | 67 | sys.stderr.write( |
| 50 | 'This server does not possess pynotify capability\n') | 68 | 'This server does not possess pynotify capability\n') |
| 51 | sys.stderr.write( | 69 | sys.stderr.write( |
| 52 | 'Remove the instance {0}'.format(self.__name)) | 70 | 'Remove the instance {0}'.format(self.__name)) |
| 53 | sys.stderr.write("or define it with 'osd_system': 'none'\n") | 71 | sys.stderr.write("or define it with 'osd_system': 'none'\n") |
| 54 | sys.exit(1) | 72 | sys.exit(errno.EPERM) |
| 55 | 73 | ||
| 56 | try: | 74 | try: |
| 57 | self.__osd_notification = pynotify.Notification(' ') | 75 | self.__osd_notification = pynotify.Notification(' ') |
| 58 | self.__osd_notification.set_timeout( | 76 | self.__osd_notification.set_timeout( |
| 59 | instance_dict['osd_timeout']) | 77 | int(instance_dict.get('osd_timeout', 5000))) |
| 60 | self.__osd_notification.set_property( | 78 | self.__osd_notification.set_property( |
| 61 | 'app_name', | 79 | 'app_name', |
| 62 | '{0} {1}'.format(sys.argv[0], __version__)) | 80 | '{0} {1}'.format(sys.argv[0], __version__)) |
| 63 | except Exception as e: | 81 | except Exception as e: |
| 64 | sys.stderr.write( | 82 | sys.stderr.write( |
| 65 | 'Unable to set up a notification object for {0} ({1})\n') | 83 | 'Unable to set up a notification object for {0} ({1})\n') |
| 66 | sys.exit(1) | 84 | sys.exit(errno.EPERM) |
| 67 | 85 | ||
| 68 | self.__osd_type = BEINC_OSD_TYPE_PYNOTIFY | 86 | self.__osd_type = BEINC_OSD_TYPE_PYNOTIFY |
| 69 | 87 | ||
| 88 | |||
| 70 | @property | 89 | @property |
| 71 | def name(self): | 90 | def name(self): |
| 72 | """ | 91 | """ |
| @@ -129,34 +148,25 @@ def beinc_instance_login(method): | |||
| 129 | """ | 148 | """ |
| 130 | decorator for checking login credentials | 149 | decorator for checking login credentials |
| 131 | """ | 150 | """ |
| 132 | from functools import wraps | ||
| 133 | 151 | ||
| 134 | @wraps(method) | 152 | def wrapper(self, *args, **kwargs): |
| 135 | def tmp_func(self, *args, **kwargs): | ||
| 136 | 153 | ||
| 137 | if not args: | 154 | if not args: |
| 138 | raise cherrypy.HTTPError(400) | 155 | raise cherrypy.HTTPError(status = 404) |
| 139 | |||
| 140 | print('args: {0}'.format(args)) | ||
| 141 | print('kwargs: {0}'.format(kwargs)) | ||
| 142 | print(cherrypy.request.config) | ||
| 143 | print('args[0]: {0} ({1})'.format(args[0], str(type(args[0])))) | ||
| 144 | print('instances: {0}'.format(str(self.__instances))) | ||
| 145 | 156 | ||
| 146 | try: | 157 | try: |
| 147 | instance = self.__instances[args[0]] | 158 | instance = self.instances[args[0]] |
| 148 | except Exception as e: | 159 | except Exception as e: |
| 149 | sys.stderr.write('Wrong instance or password: {0}\n'.format(e)) | 160 | raise cherrypy.HTTPError(status = 401, |
| 150 | raise cherrypy.HTTPError('403 Forbidden', | 161 | message = 'Wrong instance or password') |
| 151 | 'Wrong instance or password') | ||
| 152 | 162 | ||
| 153 | if not instance.password_match(kwargs.get('password')): | 163 | if not instance.password_match(kwargs.get('password')): |
| 154 | raise cherrypy.HTTPError('403 Forbidden', | 164 | raise cherrypy.HTTPError(status = 401, |
| 155 | 'Wrong instance or password') | 165 | message = 'Wrong instance or password') |
| 156 | 166 | ||
| 157 | return method(self, *args, **kwargs) | 167 | return method(self, *args, **kwargs) |
| 158 | 168 | ||
| 159 | return tmp_func | 169 | return wrapper |
| 160 | 170 | ||
| 161 | 171 | ||
| 162 | class WebNotifyServer(object): | 172 | class WebNotifyServer(object): |
| @@ -167,15 +177,32 @@ class WebNotifyServer(object): | |||
| 167 | self.__config = config | 177 | self.__config = config |
| 168 | self.__instances = dict() | 178 | self.__instances = dict() |
| 169 | 179 | ||
| 180 | # initialize pynotify if the module exists and if needed | ||
| 181 | if pynotify: | ||
| 182 | for instance in self.__config['server']['instances']: | ||
| 183 | # check if we have at least one instance that uses pynotify | ||
| 184 | # before initializing it | ||
| 185 | if instance.get('osd_system', '').lower() == 'pynotify': | ||
| 186 | if not pynotify.init('BEINC Notify'): | ||
| 187 | sys.stderr.write('pynotify.init failed! Exiting...\n') | ||
| 188 | sys.exit(1) | ||
| 189 | break | ||
| 170 | try: | 190 | try: |
| 171 | for instance in self.__config['server']['instances']: | 191 | for instance in self.__config['server']['instances']: |
| 172 | self.__instances[instance['name']] = BEINCInstance(instance) | 192 | self.__instances[instance['name']] = BEINCInstance(instance) |
| 173 | print('Instance "{0}" added'.format(instance['name'])) | 193 | print('Instance "{0}" added'.format(instance['name'])) |
| 174 | 194 | ||
| 175 | except Exception as e: | 195 | except Exception as e: |
| 176 | sys.stderr.write('Unable to initialize queues: {0}\n'.format(e)) | 196 | sys.stderr.write('Unable to create instance "{0}": {1}\n'.format( |
| 197 | instance['name'], | ||
| 198 | e)) | ||
| 177 | sys.exit(1) | 199 | sys.exit(1) |
| 178 | 200 | ||
| 201 | |||
| 202 | @property | ||
| 203 | def instances(self): | ||
| 204 | return self.__instances | ||
| 205 | |||
| 179 | @cherrypy.expose | 206 | @cherrypy.expose |
| 180 | def index(self): | 207 | def index(self): |
| 181 | """ | 208 | """ |
| @@ -190,33 +217,14 @@ class WebNotifyServer(object): | |||
| 190 | """ | 217 | """ |
| 191 | return 'default' | 218 | return 'default' |
| 192 | 219 | ||
| 220 | |||
| 193 | @cherrypy.expose | 221 | @cherrypy.expose |
| 222 | @beinc_instance_login | ||
| 194 | def push(self, *args, **kwargs): | 223 | def push(self, *args, **kwargs): |
| 195 | print('push called') | ||
| 196 | |||
| 197 | if not args: | ||
| 198 | raise cherrypy.HTTPError(400) | ||
| 199 | |||
| 200 | # print('args: {0}'.format(args)) | ||
| 201 | # print('kwargs: {0}'.format(unicode(kwargs))) | ||
| 202 | # print(cherrypy.request.config) | ||
| 203 | # print('args[0]: {0} ({1})'.format(args[0], str(type(args[0])))) | ||
| 204 | # print('instances: {0}'.format(str(self.__instances))) | ||
| 205 | |||
| 206 | try: | ||
| 207 | instance = self.__instances[args[0]] | ||
| 208 | except Exception as e: | ||
| 209 | sys.stderr.write('Wrong instance or password\n') | ||
| 210 | #print('DEBUG: {0}'.format(e)) | ||
| 211 | raise cherrypy.HTTPError('403 Forbidden', | ||
| 212 | 'Wrong instance or password') | ||
| 213 | 224 | ||
| 214 | if not instance.password_match(kwargs.get('password')): | 225 | instance = self.__instances[args[0]] |
| 215 | sys.stderr.write('Wrong instance or password\n') | ||
| 216 | raise cherrypy.HTTPError('403 Forbidden', | ||
| 217 | 'Wrong instance or password') | ||
| 218 | 226 | ||
| 219 | # instance = self.__instances[args[0]] | 227 | print('**kwargs: {0}'.format(str(kwargs))) |
| 220 | title = kwargs.get('title', '') | 228 | title = kwargs.get('title', '') |
| 221 | message = kwargs.get('message', '') | 229 | message = kwargs.get('message', '') |
| 222 | try: | 230 | try: |
| @@ -229,12 +237,19 @@ class WebNotifyServer(object): | |||
| 229 | e)) | 237 | e)) |
| 230 | raise cherrypy.HTTPError(500, 'Unable to send message') | 238 | raise cherrypy.HTTPError(500, 'Unable to send message') |
| 231 | 239 | ||
| 240 | |||
| 232 | @cherrypy.expose | 241 | @cherrypy.expose |
| 233 | @beinc_instance_login | 242 | @beinc_instance_login |
| 234 | def pull(self, *args, **kwargs): | 243 | def pull(self, *args, **kwargs): |
| 235 | 244 | ||
| 236 | instance = self.__instances(args[0]) | 245 | instance = self.__instances[args[0]] |
| 237 | return 'OK' | 246 | if not instance.queueable: |
| 247 | raise cherrypy.HTTPError( | ||
| 248 | status = 405, | ||
| 249 | message = 'BEINC instance "{0}" does not support queuing'.format( | ||
| 250 | instance.name)) | ||
| 251 | |||
| 252 | return instance.get_queue() | ||
| 238 | 253 | ||
| 239 | 254 | ||
| 240 | def main(): | 255 | def main(): |
| @@ -283,7 +298,7 @@ def main(): | |||
| 283 | except Exception as e: | 298 | except Exception as e: |
| 284 | sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, | 299 | sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, |
| 285 | e)) | 300 | e)) |
| 286 | sys.exit(1) | 301 | sys.exit(errno.EIO) |
| 287 | 302 | ||
| 288 | cherrypy.config.update({ | 303 | cherrypy.config.update({ |
| 289 | 'server.socket_host': args.hostname, | 304 | 'server.socket_host': args.hostname, |
| @@ -292,20 +307,11 @@ def main(): | |||
| 292 | 'server.ssl_certificate': config_dict['server']['general']['ssl_certificate'], | 307 | 'server.ssl_certificate': config_dict['server']['general']['ssl_certificate'], |
| 293 | 'server.ssl_private_key': config_dict['server']['general']['ssl_private_key'], | 308 | 'server.ssl_private_key': config_dict['server']['general']['ssl_private_key'], |
| 294 | 'tools.encode.on': True, | 309 | 'tools.encode.on': True, |
| 295 | 'tools.encode.encoding': 'utf-8' | 310 | 'tools.encode.encoding': 'utf-8', |
| 311 | 'tools.log_tracebacks.on': False, | ||
| 312 | 'request.show_tracebacks': False | ||
| 296 | }) | 313 | }) |
| 297 | 314 | ||
| 298 | global pynotify | ||
| 299 | try: | ||
| 300 | import pynotify | ||
| 301 | if not pynotify.init('BEINC Notify'): | ||
| 302 | sys.stderr.write('pynotify.init failed! Exiting...\n') | ||
| 303 | sys.exit(1) | ||
| 304 | except Exception as e: | ||
| 305 | sys.stderr.write( | ||
| 306 | 'Notice: pynotify support unavailable ({0})\n'.format(e)) | ||
| 307 | pynotify = False | ||
| 308 | |||
| 309 | try: | 315 | try: |
| 310 | cherrypy.quickstart(WebNotifyServer(config_dict)) | 316 | cherrypy.quickstart(WebNotifyServer(config_dict)) |
| 311 | 317 | ||
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 | |||
| 4 | import datetime | 21 | import datetime |
| 5 | import httplib | 22 | import httplib |
| 6 | import json | 23 | import json |
| @@ -14,6 +31,12 @@ import urllib2 | |||
| 14 | 31 | ||
| 15 | import weechat | 32 | import weechat |
| 16 | 33 | ||
| 34 | |||
| 35 | __author__ = 'Simeon Simeonov' | ||
| 36 | __version__ = '1.0' | ||
| 37 | __license__ = 'GPL3' | ||
| 38 | |||
| 39 | |||
| 17 | enabled = True | 40 | enabled = True |
| 18 | global_values = dict() | 41 | global_values = dict() |
| 19 | 42 | ||
| @@ -26,14 +49,13 @@ BEINC_POLICY_LIST_ONLY = 2 | |||
| 26 | 49 | ||
| 27 | class ValidHTTPSConnection(httplib.HTTPConnection): | 50 | class 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 | ||
| 50 | class ValidHTTPSHandler(urllib2.HTTPSHandler): | 72 | class 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 | ||
| 218 | def beinc_send_message(message): | 316 | def 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 | ||
| 326 | def 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 | ||
| 222 | def 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 | |||
| 367 | def 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 | ||
diff --git a/bosd.py b/bosd.py deleted file mode 100644 index d678754..0000000 --- a/bosd.py +++ /dev/null | |||
| @@ -1,101 +0,0 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | import urllib | ||
| 5 | import urllib2 | ||
| 6 | |||
| 7 | import weechat | ||
| 8 | |||
| 9 | #### CONFIG #### | ||
| 10 | enabled = True | ||
| 11 | |||
| 12 | notify_channels = [ | ||
| 13 | "RedpillLinpro.#python", | ||
| 14 | "Exile.#hin", | ||
| 15 | "RedpillLinpro.#adult", | ||
| 16 | "Exile.#pichove" | ||
| 17 | ] | ||
| 18 | |||
| 19 | connections = [ {'bosd_url': 'https://127.0.0.1:9999/npush/', | ||
| 20 | 'bosd_password': '1234'}, | ||
| 21 | |||
| 22 | # {'bosd_url': 'https://pichove.org:9999/npush/', | ||
| 23 | # 'bosd_port': 9999, | ||
| 24 | # 'bosd_password': 'foo'}, | ||
| 25 | ] | ||
| 26 | ################ | ||
| 27 | |||
| 28 | __VERSION__ = '2.0' | ||
| 29 | |||
| 30 | def bosd_send_message(header='', message=''): | ||
| 31 | for conn in connections: | ||
| 32 | try: | ||
| 33 | conn['nheader'] = header # notification header | ||
| 34 | conn['nmessage'] = message # notification message | ||
| 35 | |||
| 36 | data = urllib.urlencode(conn) | ||
| 37 | |||
| 38 | req = urllib2.Request(url, data) | ||
| 39 | urllib2.urlopen(req) | ||
| 40 | #weechat.prnt("", message) | ||
| 41 | except: | ||
| 42 | continue | ||
| 43 | |||
| 44 | return True | ||
| 45 | |||
| 46 | |||
| 47 | def bosd_command(data, buffer, args): | ||
| 48 | global enabled | ||
| 49 | if args == 'on': | ||
| 50 | enabled = True | ||
| 51 | weechat.prnt(weechat.current_buffer(), "bosd on") | ||
| 52 | elif args == 'off': | ||
| 53 | enabled = False | ||
| 54 | weechat.prnt(weechat.current_buffer(), "bosd off") | ||
| 55 | else: | ||
| 56 | bosd_send_message('BOSD generic', args) | ||
| 57 | |||
| 58 | return weechat.WEECHAT_RC_OK | ||
| 59 | |||
| 60 | |||
| 61 | def bosd_privmsg_handler(data, signal, signal_data): | ||
| 62 | if not enabled: | ||
| 63 | return weechat.WEECHAT_RC_OK | ||
| 64 | |||
| 65 | prvmsg_dict = weechat.info_get_hashtable("irc_message_parse", | ||
| 66 | { "message": signal_data }) | ||
| 67 | |||
| 68 | |||
| 69 | server = signal.split(",")[0] | ||
| 70 | |||
| 71 | # channel = signal_data.split(":")[-1] | ||
| 72 | # my_nick = weechat.info_get("irc_nick_from_host", signal_data) | ||
| 73 | |||
| 74 | my_nick = weechat.info_get("irc_nick", server) | ||
| 75 | channel = prvmsg_dict['arguments'].split(":")[0].strip() | ||
| 76 | nick = prvmsg_dict['nick'] | ||
| 77 | message = ':'.join(prvmsg_dict['arguments'].split(':')[1:]).strip() | ||
| 78 | |||
| 79 | if (my_nick in message) or ('{0}.{1}'.format(server,channel) in notify_channels): | ||
| 80 | template_msg = '{0} @ {1} - {2}: {3}'.format(channel, | ||
| 81 | server, | ||
| 82 | nick, | ||
| 83 | message) | ||
| 84 | bosd_send_xosd_message(template_msg) | ||
| 85 | |||
| 86 | if my_nick in channel: | ||
| 87 | template_msg = '{0} @ {1}: {2}'.format(nick, server, message) | ||
| 88 | bosd_send_xosd_message(template_msg) | ||
| 89 | |||
| 90 | #my_str = "%s - %s - %s - %s" % (my_nick, server, channel, message) | ||
| 91 | #weechat.prnt("", my_str) | ||
| 92 | |||
| 93 | return weechat.WEECHAT_RC_OK | ||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | weechat.register('bosd', 'Simeon Simeonov', '1.0', 'GPL3', 'Socket notification script', "", "") | ||
| 98 | weechat.hook_command("bosd", "bosd on off toggle", "<on | off>", "description...", "None", "bosd_command", "") | ||
| 99 | weechat.hook_signal("*,irc_in2_privmsg", "bosd_privmsg_handler", "") | ||
| 100 | |||
| 101 | weechat.prnt("", "bosd initiated!") | ||
diff --git a/bpynotify_orm.py b/bpynotify_orm.py deleted file mode 100644 index 0540d0a..0000000 --- a/bpynotify_orm.py +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | |||
| 3 | import datetime | ||
| 4 | |||
| 5 | from sqlalchemy import schema, types | ||
| 6 | from sqlalchemy.orm import mapper | ||
| 7 | |||
| 8 | metadata = schema.MetaData() | ||
| 9 | |||
| 10 | notification_entry_table = schema.Table('notification_entries', metadata, | ||
| 11 | schema.Column('id', types.Integer, primary_key=True), | ||
| 12 | schema.Column('timestamp', types.DateTime), | ||
| 13 | schema.Column('message', types.Unicode(), default = u'Empty message'), | ||
| 14 | schema.Column('viewed', types.Boolean, default=False) | ||
| 15 | ) | ||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | class NotificationEntry(object): | ||
| 20 | |||
| 21 | def __init__(self, message): | ||
| 22 | |||
| 23 | self.timestamp = datetime.datetime.now() | ||
| 24 | self.message = message | ||
| 25 | |||
| 26 | |||
| 27 | def __repr__(self): | ||
| 28 | |||
| 29 | return message | ||
| 30 | |||
| 31 | |||
| 32 | mapper(NotificationEntry, notification_entry_table) | ||
diff --git a/socket_server.py b/socket_server.py deleted file mode 100755 index c68e500..0000000 --- a/socket_server.py +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | import SocketServer | ||
| 5 | import os | ||
| 6 | import sys | ||
| 7 | |||
| 8 | from sqlalchemy import create_engine | ||
| 9 | from sqlalchemy.orm import sessionmaker | ||
| 10 | |||
| 11 | from bpynotify_orm import NotificationEntry, notification_entry_table | ||
| 12 | |||
| 13 | |||
| 14 | class CustomSocketServer(SocketServer.TCPServer): | ||
| 15 | |||
| 16 | def __init__(self, server_address, RequestHandlerClass, session): | ||
| 17 | #super(CustomSocketServer, self).__init__(server_address, | ||
| 18 | # RequestHandlerClass) | ||
| 19 | SocketServer.TCPServer.__init__(self, | ||
| 20 | server_address, | ||
| 21 | RequestHandlerClass) | ||
| 22 | self.session = session | ||
| 23 | |||
| 24 | |||
| 25 | class MyTCPHandler(SocketServer.StreamRequestHandler): | ||
| 26 | """ | ||
| 27 | The RequestHandler class for our server. | ||
| 28 | |||
| 29 | It is instantiated once per connection to the server, and must | ||
| 30 | override the handle() method to implement communication to the | ||
| 31 | client. | ||
| 32 | """ | ||
| 33 | |||
| 34 | def handle(self): | ||
| 35 | |||
| 36 | self.data = self.rfile.readline().decode('utf8').strip() | ||
| 37 | #print(self.data) | ||
| 38 | entry = NotificationEntry(self.data) | ||
| 39 | self.server.session.add(entry) | ||
| 40 | self.server.session.commit() | ||
| 41 | |||
| 42 | |||
| 43 | def main(): | ||
| 44 | |||
| 45 | HOST, PORT = "localhost", 9997 | ||
| 46 | |||
| 47 | if len(sys.argv) == 2 and sys.argv[1] == '-d': | ||
| 48 | try: | ||
| 49 | pid = os.fork() | ||
| 50 | if pid > 0: | ||
| 51 | sys.exit(0) | ||
| 52 | except Exception as e: | ||
| 53 | sys.stderr.write("Unable to daemonize. Fork error: {0}".format(e)) | ||
| 54 | sys.exit(1) | ||
| 55 | |||
| 56 | try: | ||
| 57 | |||
| 58 | engine = create_engine('sqlite:///notifications.db') | ||
| 59 | Session = sessionmaker(bind=engine) # bound session | ||
| 60 | session = Session() | ||
| 61 | |||
| 62 | server = CustomSocketServer((HOST, PORT), MyTCPHandler, session) | ||
| 63 | |||
| 64 | # clean all previous entries # | ||
| 65 | notification_entry_table.delete(bind=engine).execute() | ||
| 66 | |||
| 67 | server.serve_forever() | ||
| 68 | |||
| 69 | except Exception as e: | ||
| 70 | sys.stderr.write("Server error: {0}".format(e)) | ||
| 71 | sys.exit(1) | ||
| 72 | |||
| 73 | sys.exit(0) | ||
| 74 | |||
| 75 | |||
| 76 | if __name__ == "__main__": | ||
| 77 | main() | ||
diff --git a/web_server.py b/web_server.py deleted file mode 100755 index cec8c38..0000000 --- a/web_server.py +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | import sys | ||
| 5 | |||
| 6 | import cherrypy | ||
| 7 | |||
| 8 | class WebNotifyServer(object): | ||
| 9 | |||
| 10 | def __init__(self): | ||
| 11 | self.counter = 0 | ||
| 12 | |||
| 13 | @cherrypy.expose | ||
| 14 | def notifications(self, **kwargs): | ||
| 15 | self.counter += 1 | ||
| 16 | print(str(kwargs)) | ||
| 17 | print(self.counter) | ||
| 18 | return ("called") | ||
| 19 | |||
| 20 | |||
| 21 | @cherrypy.expose | ||
| 22 | def other(self, **kwargs): | ||
| 23 | self.counter += 1 | ||
| 24 | print(str(kwargs)) | ||
| 25 | print(self.counter) | ||
| 26 | return ("called") | ||
| 27 | |||
| 28 | |||
| 29 | def main(): | ||
| 30 | |||
| 31 | cherrypy.config.update({ | ||
| 32 | 'server.socket_host': '10.0.0.4', | ||
| 33 | 'server.socket_port': 40003, | ||
| 34 | }) | ||
| 35 | |||
| 36 | try: | ||
| 37 | cherrypy.quickstart(WebNotifyServer()) | ||
| 38 | |||
| 39 | except Exception as e: | ||
| 40 | sys.stderr.write("WebServer error: {0}".format(e)) | ||
| 41 | sys.exit(1) | ||
| 42 | |||
| 43 | sys.exit(0) | ||
| 44 | |||
| 45 | |||
| 46 | if __name__ == "__main__": | ||
| 47 | main() | ||
diff --git a/weechat_notify.py b/weechat_notify.py deleted file mode 100755 index 897f26d..0000000 --- a/weechat_notify.py +++ /dev/null | |||
| @@ -1,49 +0,0 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | import json | ||
| 5 | import urllib2 | ||
| 6 | import sched | ||
| 7 | import sys | ||
| 8 | import time | ||
| 9 | |||
| 10 | import pynotify | ||
| 11 | |||
| 12 | POLLING_FREQUENCY = 10 | ||
| 13 | NOTIFICATION_URL = 'https://simeon.simeonov.no:40004/notifications/' | ||
| 14 | TIMEOUT_SEC = 3 | ||
| 15 | |||
| 16 | |||
| 17 | def poll_notifications(scheduler): | ||
| 18 | """ | ||
| 19 | """ | ||
| 20 | try: | ||
| 21 | req = urllib2.Request(NOTIFICATION_URL) | ||
| 22 | response = urllib2.urlopen(req) | ||
| 23 | response_str = response.read() | ||
| 24 | |||
| 25 | entries = json.loads(response_str) | ||
| 26 | for entry in entries: | ||
| 27 | n = pynotify.Notification('IRC', entry, 'dialog-information') | ||
| 28 | n.set_timeout(TIMEOUT_SEC) | ||
| 29 | n.show() | ||
| 30 | |||
| 31 | except Exception as e: | ||
| 32 | sys.stderr.write("Client error: {0}".format(e)) | ||
| 33 | sys.exit(1) | ||
| 34 | |||
| 35 | scheduler.enter(POLLING_FREQUENCY, 1, poll_notifications, (scheduler,)) | ||
| 36 | |||
| 37 | |||
| 38 | def main(): | ||
| 39 | |||
| 40 | if not pynotify.init("Weechat Notify"): | ||
| 41 | sys.stderr.write("There was a problem with libnotify") | ||
| 42 | sys.exit(1) | ||
| 43 | |||
| 44 | sc = sched.scheduler(time.time, time.sleep) | ||
| 45 | sc.enter(POLLING_FREQUENCY, 1, poll_notifications, (sc,)) | ||
| 46 | sc.run() | ||
| 47 | |||
| 48 | if __name__ == '__main__': | ||
| 49 | main() | ||
diff --git a/weechat_notify_server.py b/weechat_notify_server.py deleted file mode 100755 index 35d4f42..0000000 --- a/weechat_notify_server.py +++ /dev/null | |||
| @@ -1,116 +0,0 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | |||
| 4 | import json | ||
| 5 | import SocketServer | ||
| 6 | import os | ||
| 7 | import sys | ||
| 8 | |||
| 9 | from multiprocessing import Process, Queue | ||
| 10 | |||
| 11 | import cherrypy | ||
| 12 | |||
| 13 | import settings | ||
| 14 | |||
| 15 | |||
| 16 | class CustomSocketServer(SocketServer.TCPServer): | ||
| 17 | |||
| 18 | def __init__(self, server_address, RequestHandlerClass, queue): | ||
| 19 | SocketServer.TCPServer.__init__(self, | ||
| 20 | server_address, | ||
| 21 | RequestHandlerClass) | ||
| 22 | self.queue = queue | ||
| 23 | |||
| 24 | |||
| 25 | class MyTCPHandler(SocketServer.StreamRequestHandler): | ||
| 26 | """ | ||
| 27 | The RequestHandler class for our server. | ||
| 28 | |||
| 29 | It is instantiated once per connection to the server, and must | ||
| 30 | override the handle() method to implement communication to the | ||
| 31 | client. | ||
| 32 | """ | ||
| 33 | |||
| 34 | def handle(self): | ||
| 35 | |||
| 36 | data = self.rfile.readline().strip() | ||
| 37 | |||
| 38 | if self.server.queue.qsize() > 3: | ||
| 39 | self.server.queue.get() | ||
| 40 | |||
| 41 | self.server.queue.put(data) | ||
| 42 | #print(self.data) | ||
| 43 | |||
| 44 | |||
| 45 | class WebNotifyServer(object): | ||
| 46 | |||
| 47 | def __init__(self, queue): | ||
| 48 | self.queue = queue | ||
| 49 | |||
| 50 | @cherrypy.expose | ||
| 51 | def notifications(self): | ||
| 52 | |||
| 53 | entry_list = list() | ||
| 54 | |||
| 55 | while not self.queue.empty(): | ||
| 56 | entry_list.append(self.queue.get(False)) | ||
| 57 | |||
| 58 | #print (json.dumps(entry_list, sort_keys=True, indent=4)) | ||
| 59 | return json.dumps(entry_list) | ||
| 60 | |||
| 61 | |||
| 62 | def socket_server(queue): | ||
| 63 | |||
| 64 | try: | ||
| 65 | server = CustomSocketServer((settings.SOCKET_SERVER_HOST, | ||
| 66 | settings.SOCKET_SERVER_PORT), | ||
| 67 | MyTCPHandler, | ||
| 68 | queue) | ||
| 69 | server.serve_forever() | ||
| 70 | |||
| 71 | except Exception as e: | ||
| 72 | sys.stderr.write("SocketServer error: {0}".format(e)) | ||
| 73 | sys.exit(1) | ||
| 74 | |||
| 75 | sys.exit(0) | ||
| 76 | |||
| 77 | |||
| 78 | |||
| 79 | def web_server(queue): | ||
| 80 | |||
| 81 | cherrypy.config.update({ | ||
| 82 | 'server.socket_host': settings.WEB_SERVER_HOST, | ||
| 83 | 'server.socket_port': settings.WEB_SERVER_PORT, | ||
| 84 | 'server.ssl_module': 'pyopenssl', | ||
| 85 | 'server.ssl_certificate': settings.WEB_SERVER_CERT, | ||
| 86 | 'server.ssl_private_key': settings.WEB_SERVER_KEY, | ||
| 87 | }) | ||
| 88 | |||
| 89 | try: | ||
| 90 | |||
| 91 | cherrypy.quickstart(WebNotifyServer(queue)) | ||
| 92 | |||
| 93 | except Exception as e: | ||
| 94 | sys.stderr.write("WebServer error: {0}".format(e)) | ||
| 95 | sys.exit(1) | ||
| 96 | |||
| 97 | sys.exit(0) | ||
| 98 | |||
| 99 | |||
| 100 | def main(): | ||
| 101 | |||
| 102 | q = Queue() | ||
| 103 | |||
| 104 | socket_server_process = Process(target=socket_server, args=(q,)) | ||
| 105 | web_server_process = Process(target=web_server, args=(q,)) | ||
| 106 | |||
| 107 | web_server_process.start() | ||
| 108 | socket_server_process.start() | ||
| 109 | |||
| 110 | socket_server_process.join() | ||
| 111 | web_server_process.join() | ||
| 112 | |||
| 113 | sys.exit(0) | ||
| 114 | |||
| 115 | if __name__ == "__main__": | ||
| 116 | main() | ||
