From b9f8e6e78c3f4fdbe7082416e75109c25515d0ed Mon Sep 17 00:00:00 2001 From: Walter Purcaro Date: Mon, 14 Dec 2015 03:22:27 +0100 Subject: Rename IRC and XMPP addon plugins --- module/plugins/hooks/IRC.py | 439 ++++++++++++++++++++++++++++++++++ module/plugins/hooks/IRCInterface.py | 438 --------------------------------- module/plugins/hooks/XMPP.py | 276 +++++++++++++++++++++ module/plugins/hooks/XMPPInterface.py | 278 --------------------- 4 files changed, 715 insertions(+), 716 deletions(-) create mode 100644 module/plugins/hooks/IRC.py delete mode 100644 module/plugins/hooks/IRCInterface.py create mode 100644 module/plugins/hooks/XMPP.py delete mode 100644 module/plugins/hooks/XMPPInterface.py (limited to 'module/plugins/hooks') diff --git a/module/plugins/hooks/IRC.py b/module/plugins/hooks/IRC.py new file mode 100644 index 000000000..2b7bea5fd --- /dev/null +++ b/module/plugins/hooks/IRC.py @@ -0,0 +1,439 @@ +# -*- coding: utf-8 -*- + +import re +import select +import socket +import ssl +import time +import traceback + +import pycurl + +from threading import Thread + +from module.Api import PackageDoesNotExists, FileDoesNotExists +from module.plugins.internal.Notifier import Notifier +from module.internal.misc import formatSize + + +class IRC(Thread, Notifier): + __name__ = "IRC" + __type__ = "hook" + __version__ = "0.19" + __status__ = "testing" + + __config__ = [("activated", "bool", "Activated" , False ), + ("host" , "str" , "IRC-Server Address" , "Enter your server here!"), + ("port" , "int" , "IRC-Server Port" , 6667 ), + ("ident" , "str" , "Clients ident" , "pyload-irc" ), + ("realname" , "str" , "Realname" , "pyload-irc" ), + ("ssl" , "bool", "Use SSL" , False ), + ("nick" , "str" , "Nickname the Client will take" , "pyLoad-IRC" ), + ("owner" , "str" , "Nickname the Client will accept commands from", "Enter your nick here!" ), + ("info_file", "bool", "Inform about every file finished" , False ), + ("info_pack", "bool", "Inform about every package finished" , True ), + ("captcha" , "bool", "Send captcha requests" , True )] + + __description__ = """Connect to irc and let owner perform different tasks""" + __license__ = "GPLv3" + __authors__ = [("Jeix", "Jeix@hasnomail.com")] + + + def __init__(self, *args, **kwargs): + Thread.__init__(self) + Addon.__init__(self, *args, **kwargs) + self.setDaemon(True) + + + def activate(self): + self.abort = False + self.more = [] + self.new_package = {} + + self.start() + + + def package_finished(self, pypack): + try: + if self.config.get('info_pack'): + self.response(_("Package finished: %s") % pypack.name) + + except Exception: + pass + + + def download_finished(self, pyfile): + try: + if self.config.get('info_file'): + self.response( + _("Download finished: %(name)s @ %(plugin)s ") % {'name': pyfile.name, 'plugin': pyfile.pluginname}) + + except Exception: + pass + + + def captcha_task(self, task): + if self.config.get('captcha') and task.isTextual(): + task.handler.append(self) + task.setWaiting(60) + + html = self.load("http://www.freeimagehosting.net/upload.php", + post={'attached': (pycurl.FORM_FILE, task.captchaFile)}) + + url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", html).group(1) + self.response(_("New Captcha Request: %s") % url) + self.response(_("Answer with 'c %s text on the captcha'") % task.id) + + + def run(self): + #: Connect to IRC etc. + self.sock = socket.socket() + host = self.config.get('host') + self.sock.connect((host, self.config.get('port'))) + + if self.config.get('ssl'): + self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support certificate + + nick = self.config.get('nick') + self.sock.send("NICK %s\r\n" % nick) + self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) + for t in self.config.get('owner').split(): + if t.strip().startswith("#"): + self.sock.send("JOIN %s\r\n" % t.strip()) + self.log_info(_("Connected to"), host) + self.log_info(_("Switching to listening mode!")) + try: + self.main_loop() + + except IRCError, ex: + self.sock.send("QUIT :byebye\r\n") + if self.pyload.debug: + traceback.print_exc() + self.sock.close() + + + def main_loop(self): + readbuffer = "" + while True: + time.sleep(1) + fdset = select.select([self.sock], [], [], 0) + if self.sock not in fdset[0]: + continue + + if self.abort: + raise IRCError("quit") + + readbuffer += self.sock.recv(1024) + temp = readbuffer.split("\n") + readbuffer = temp.pop() + + for line in temp: + line = line.rstrip() + first = line.split() + + if first[0] == "PING": + self.sock.send("PONG %s\r\n" % first[1]) + + if first[0] == "ERROR": + raise IRCError(line) + + msg = line.split(None, 3) + if len(msg) < 4: + continue + + msg = { + 'origin': msg[0][1:], + 'action': msg[1], + 'target': msg[2], + 'text': msg[3][1:] + } + + self.handle_events(msg) + + + def handle_events(self, msg): + if not msg['origin'].split("!", 1)[0] in self.config.get('owner').split(): + return + + if msg['target'].split("!", 1)[0] is not self.config.get('nick'): + return + + if msg['action'] != "PRIVMSG": + return + + #: HANDLE CTCP ANTI FLOOD/BOT PROTECTION + if msg['text'] == "\x01VERSION\x01": + self.log_debug("Sending CTCP VERSION") + self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) + return + elif msg['text'] == "\x01TIME\x01": + self.log_debug("Sending CTCP TIME") + self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) + return + elif msg['text'] == "\x01LAG\x01": + self.log_debug("Received CTCP LAG") #: don't know how to answer + return + + trigger = "pass" + args = None + + try: + temp = msg['text'].split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + + except Exception: + pass + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + self.response(line, msg['origin']) + + except Exception, e: + self.log_error(e, trace=True) + + + def response(self, msg, origin=""): + if origin == "": + for t in self.config.get('owner').split(): + self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) + else: + self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) + + + #### Events + + def event_pass(self, args): + return [] + + + def event_status(self, args): + downloads = self.pyload.api.statusDownloads() + if not downloads: + return ["INFO: There are no active downloads currently."] + + temp_progress = "" + lines = ["ID - Name - Status - Speed - ETA - Progress"] + for data in downloads: + + if data.status == 5: + temp_progress = data.format_wait + else: + temp_progress = "%d%% (%s)" % (data.percent, data.format_size) + + lines.append("#%d - %s - %s - %s - %s - %s" % + ( + data.fid, + data.name, + data.statusmsg, + "%s/s" % formatSize(data.speed), + "%s" % data.format_eta, + temp_progress + )) + return lines + + + def event_queue(self, args): + pdata = self.pyload.api.getQueueData() + + if not pdata: + return ["INFO: There are no packages in queue."] + + lines = [] + for pack in pdata: + lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + + return lines + + + def event_collector(self, args): + pdata = self.pyload.api.getCollectorData() + if not pdata: + return ["INFO: No packages in collector!"] + + lines = [] + for pack in pdata: + lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + + return lines + + + def event_info(self, args): + if not args: + return ["ERROR: Use info like this: info "] + + info = None + try: + info = self.pyload.api.getFileData(int(args[0])) + + except FileDoesNotExists: + return ["ERROR: Link doesn't exists."] + + return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)] + + + def event_packinfo(self, args): + if not args: + return ["ERROR: Use packinfo like this: packinfo "] + + lines = [] + pack = None + try: + pack = self.pyload.api.getPackageData(int(args[0])) + + except PackageDoesNotExists: + return ["ERROR: Package doesn't exists."] + + id = args[0] + + self.more = [] + + lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links))) + for pyfile in pack.links: + self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size, + pyfile.statusmsg, pyfile.plugin)) + + if len(self.more) < 6: + lines.extend(self.more) + self.more = [] + else: + lines.extend(self.more[:6]) + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + return lines + + + def event_more(self, args): + if not self.more: + return ["No more information to display."] + + lines = self.more[:6] + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + return lines + + + def event_start(self, args): + self.pyload.api.unpauseServer() + return ["INFO: Starting downloads."] + + + def event_stop(self, args): + self.pyload.api.pauseServer() + return ["INFO: No new downloads will be started."] + + + def event_add(self, args): + if len(args) < 2: + return ['ERROR: Add links like this: "add links". ', + "This will add the link to to the package / the package with id !"] + + pack = args[0].strip() + links = [x.strip() for x in args[1:]] + + count_added = 0 + count_failed = 0 + try: + id = int(pack) + pack = self.pyload.api.getPackageData(id) + if not pack: + return ["ERROR: Package doesn't exists."] + + #@TODO: add links + + return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] + + except Exception: + #: Create new package + id = self.pyload.api.addPackage(pack, links, 1) + return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] + + + def event_del(self, args): + if len(args) < 2: + return ["ERROR: Use del command like this: del -p|-l [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + if args[0] == "-p": + ret = self.pyload.api.deletePackages(map(int, args[1:])) + return ["INFO: Deleted %d packages!" % len(args[1:])] + + elif args[0] == "-l": + ret = self.pyload.api.delLinks(map(int, args[1:])) + return ["INFO: Deleted %d links!" % len(args[1:])] + + else: + return ["ERROR: Use del command like this: del <-p|-l> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + + def event_push(self, args): + if not args: + return ["ERROR: Push package to queue like this: push "] + + id = int(args[0]) + try: + info = self.pyload.api.getPackageInfo(id) + except PackageDoesNotExists: + return ["ERROR: Package #%d does not exist." % id] + + self.pyload.api.pushToQueue(id) + return ["INFO: Pushed package #%d to queue." % id] + + + def event_pull(self, args): + if not args: + return ["ERROR: Pull package from queue like this: pull ."] + + id = int(args[0]) + if not self.pyload.api.getPackageData(id): + return ["ERROR: Package #%d does not exist." % id] + + self.pyload.api.pullFromQueue(id) + return ["INFO: Pulled package #%d from queue to collector." % id] + + + def event_c(self, args): + """ + Captcha answer + """ + if not args: + return ["ERROR: Captcha ID missing."] + + task = self.pyload.captchaManager.getTaskByID(args[0]) + if not task: + return ["ERROR: Captcha Task with ID %s does not exists." % args[0]] + + task.setResult(" ".join(args[1:])) + return ["INFO: Result %s saved." % " ".join(args[1:])] + + + def event_help(self, args): + lines = ["The following commands are available:", + "add [...] Adds link to package. (creates new package if it does not exist)", + "queue Shows all packages in the queue", + "collector Shows all packages in collector", + "del -p|-l [...] Deletes all packages|links with the ids specified", + "info Shows info of the link with id ", + "packinfo Shows info of the package with id ", + "more Shows more info when the result was truncated", + "start Starts all downloads", + "stop Stops the download (but not abort active downloads)", + "push Push package to queue", + "pull Pull package from queue", + "status Show general download status", + "help Shows this help message"] + return lines + + +class IRCError(Exception): + + def __init__(self, value): + self.value = value + + + def __str__(self): + return repr(self.value) diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py deleted file mode 100644 index 1f337d686..000000000 --- a/module/plugins/hooks/IRCInterface.py +++ /dev/null @@ -1,438 +0,0 @@ -# -*- coding: utf-8 -*- - -import pycurl -import re -import select -import socket -import ssl -import time -import traceback - -from threading import Thread - -from module.Api import PackageDoesNotExists, FileDoesNotExists -from module.plugins.internal.Addon import Addon -from module.utils import formatSize - - -class IRCInterface(Thread, Addon): - __name__ = "IRCInterface" - __type__ = "hook" - __version__ = "0.18" - __status__ = "testing" - - __config__ = [("activated", "bool", "Activated" , False ), - ("host" , "str" , "IRC-Server Address" , "Enter your server here!"), - ("port" , "int" , "IRC-Server Port" , 6667 ), - ("ident" , "str" , "Clients ident" , "pyload-irc" ), - ("realname" , "str" , "Realname" , "pyload-irc" ), - ("ssl" , "bool", "Use SSL" , False ), - ("nick" , "str" , "Nickname the Client will take" , "pyLoad-IRC" ), - ("owner" , "str" , "Nickname the Client will accept commands from", "Enter your nick here!" ), - ("info_file", "bool", "Inform about every file finished" , False ), - ("info_pack", "bool", "Inform about every package finished" , True ), - ("captcha" , "bool", "Send captcha requests" , True )] - - __description__ = """Connect to irc and let owner perform different tasks""" - __license__ = "GPLv3" - __authors__ = [("Jeix", "Jeix@hasnomail.com")] - - - def __init__(self, *args, **kwargs): - Thread.__init__(self) - Addon.__init__(self, *args, **kwargs) - self.setDaemon(True) - - - def activate(self): - self.abort = False - self.more = [] - self.new_package = {} - - self.start() - - - def package_finished(self, pypack): - try: - if self.get_config('info_pack'): - self.response(_("Package finished: %s") % pypack.name) - - except Exception: - pass - - - def download_finished(self, pyfile): - try: - if self.get_config('info_file'): - self.response( - _("Download finished: %(name)s @ %(plugin)s ") % {'name': pyfile.name, 'plugin': pyfile.pluginname}) - - except Exception: - pass - - - def captcha_task(self, task): - if self.get_config('captcha') and task.isTextual(): - task.handler.append(self) - task.setWaiting(60) - - html = self.load("http://www.freeimagehosting.net/upload.php", - post={'attached': (pycurl.FORM_FILE, task.captchaFile)}) - - url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", html).group(1) - self.response(_("New Captcha Request: %s") % url) - self.response(_("Answer with 'c %s text on the captcha'") % task.id) - - - def run(self): - #: Connect to IRC etc. - self.sock = socket.socket() - host = self.get_config('host') - self.sock.connect((host, self.get_config('port'))) - - if self.get_config('ssl'): - self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support certificate - - nick = self.get_config('nick') - self.sock.send("NICK %s\r\n" % nick) - self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) - for t in self.get_config('owner').split(): - if t.strip().startswith("#"): - self.sock.send("JOIN %s\r\n" % t.strip()) - self.log_info(_("Connected to"), host) - self.log_info(_("Switching to listening mode!")) - try: - self.main_loop() - - except IRCError, ex: - self.sock.send("QUIT :byebye\r\n") - if self.pyload.debug: - traceback.print_exc() - self.sock.close() - - - def main_loop(self): - readbuffer = "" - while True: - time.sleep(1) - fdset = select.select([self.sock], [], [], 0) - if self.sock not in fdset[0]: - continue - - if self.abort: - raise IRCError("quit") - - readbuffer += self.sock.recv(1024) - temp = readbuffer.split("\n") - readbuffer = temp.pop() - - for line in temp: - line = line.rstrip() - first = line.split() - - if first[0] == "PING": - self.sock.send("PONG %s\r\n" % first[1]) - - if first[0] == "ERROR": - raise IRCError(line) - - msg = line.split(None, 3) - if len(msg) < 4: - continue - - msg = { - 'origin': msg[0][1:], - 'action': msg[1], - 'target': msg[2], - 'text': msg[3][1:] - } - - self.handle_events(msg) - - - def handle_events(self, msg): - if not msg['origin'].split("!", 1)[0] in self.get_config('owner').split(): - return - - if msg['target'].split("!", 1)[0] is not self.get_config('nick'): - return - - if msg['action'] != "PRIVMSG": - return - - #: HANDLE CTCP ANTI FLOOD/BOT PROTECTION - if msg['text'] == "\x01VERSION\x01": - self.log_debug("Sending CTCP VERSION") - self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) - return - elif msg['text'] == "\x01TIME\x01": - self.log_debug("Sending CTCP TIME") - self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) - return - elif msg['text'] == "\x01LAG\x01": - self.log_debug("Received CTCP LAG") #: don't know how to answer - return - - trigger = "pass" - args = None - - try: - temp = msg['text'].split() - trigger = temp[0] - if len(temp) > 1: - args = temp[1:] - - except Exception: - pass - - handler = getattr(self, "event_%s" % trigger, self.event_pass) - try: - res = handler(args) - for line in res: - self.response(line, msg['origin']) - - except Exception, e: - self.log_error(e, trace=True) - - - def response(self, msg, origin=""): - if origin == "": - for t in self.get_config('owner').split(): - self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) - else: - self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) - - - #### Events - - def event_pass(self, args): - return [] - - - def event_status(self, args): - downloads = self.pyload.api.statusDownloads() - if not downloads: - return ["INFO: There are no active downloads currently."] - - temp_progress = "" - lines = ["ID - Name - Status - Speed - ETA - Progress"] - for data in downloads: - - if data.status == 5: - temp_progress = data.format_wait - else: - temp_progress = "%d%% (%s)" % (data.percent, data.format_size) - - lines.append("#%d - %s - %s - %s - %s - %s" % - ( - data.fid, - data.name, - data.statusmsg, - "%s/s" % formatSize(data.speed), - "%s" % data.format_eta, - temp_progress - )) - return lines - - - def event_queue(self, args): - ps = self.pyload.api.getQueueData() - - if not ps: - return ["INFO: There are no packages in queue."] - - lines = [] - for pack in ps: - lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) - - return lines - - - def event_collector(self, args): - ps = self.pyload.api.getCollectorData() - if not ps: - return ["INFO: No packages in collector!"] - - lines = [] - for pack in ps: - lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) - - return lines - - - def event_info(self, args): - if not args: - return ["ERROR: Use info like this: info "] - - info = None - try: - info = self.pyload.api.getFileData(int(args[0])) - - except FileDoesNotExists: - return ["ERROR: Link doesn't exists."] - - return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)] - - - def event_packinfo(self, args): - if not args: - return ["ERROR: Use packinfo like this: packinfo "] - - lines = [] - pack = None - try: - pack = self.pyload.api.getPackageData(int(args[0])) - - except PackageDoesNotExists: - return ["ERROR: Package doesn't exists."] - - id = args[0] - - self.more = [] - - lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links))) - for pyfile in pack.links: - self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size, - pyfile.statusmsg, pyfile.plugin)) - - if len(self.more) < 6: - lines.extend(self.more) - self.more = [] - else: - lines.extend(self.more[:6]) - self.more = self.more[6:] - lines.append("%d more links do display." % len(self.more)) - - return lines - - - def event_more(self, args): - if not self.more: - return ["No more information to display."] - - lines = self.more[:6] - self.more = self.more[6:] - lines.append("%d more links do display." % len(self.more)) - - return lines - - - def event_start(self, args): - self.pyload.api.unpauseServer() - return ["INFO: Starting downloads."] - - - def event_stop(self, args): - self.pyload.api.pauseServer() - return ["INFO: No new downloads will be started."] - - - def event_add(self, args): - if len(args) < 2: - return ['ERROR: Add links like this: "add links". ', - "This will add the link to to the package / the package with id !"] - - pack = args[0].strip() - links = [x.strip() for x in args[1:]] - - count_added = 0 - count_failed = 0 - try: - id = int(pack) - pack = self.pyload.api.getPackageData(id) - if not pack: - return ["ERROR: Package doesn't exists."] - - #@TODO: add links - - return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] - - except Exception: - #: Create new package - id = self.pyload.api.addPackage(pack, links, 1) - return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] - - - def event_del(self, args): - if len(args) < 2: - return ["ERROR: Use del command like this: del -p|-l [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] - - if args[0] == "-p": - ret = self.pyload.api.deletePackages(map(int, args[1:])) - return ["INFO: Deleted %d packages!" % len(args[1:])] - - elif args[0] == "-l": - ret = self.pyload.api.delLinks(map(int, args[1:])) - return ["INFO: Deleted %d links!" % len(args[1:])] - - else: - return ["ERROR: Use del command like this: del <-p|-l> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] - - - def event_push(self, args): - if not args: - return ["ERROR: Push package to queue like this: push "] - - id = int(args[0]) - try: - info = self.pyload.api.getPackageInfo(id) - except PackageDoesNotExists: - return ["ERROR: Package #%d does not exist." % id] - - self.pyload.api.pushToQueue(id) - return ["INFO: Pushed package #%d to queue." % id] - - - def event_pull(self, args): - if not args: - return ["ERROR: Pull package from queue like this: pull ."] - - id = int(args[0]) - if not self.pyload.api.getPackageData(id): - return ["ERROR: Package #%d does not exist." % id] - - self.pyload.api.pullFromQueue(id) - return ["INFO: Pulled package #%d from queue to collector." % id] - - - def event_c(self, args): - """ - Captcha answer - """ - if not args: - return ["ERROR: Captcha ID missing."] - - task = self.pyload.captchaManager.getTaskByID(args[0]) - if not task: - return ["ERROR: Captcha Task with ID %s does not exists." % args[0]] - - task.setResult(" ".join(args[1:])) - return ["INFO: Result %s saved." % " ".join(args[1:])] - - - def event_help(self, args): - lines = ["The following commands are available:", - "add [...] Adds link to package. (creates new package if it does not exist)", - "queue Shows all packages in the queue", - "collector Shows all packages in collector", - "del -p|-l [...] Deletes all packages|links with the ids specified", - "info Shows info of the link with id ", - "packinfo Shows info of the package with id ", - "more Shows more info when the result was truncated", - "start Starts all downloads", - "stop Stops the download (but not abort active downloads)", - "push Push package to queue", - "pull Pull package from queue", - "status Show general download status", - "help Shows this help message"] - return lines - - -class IRCError(Exception): - - def __init__(self, value): - self.value = value - - - def __str__(self): - return repr(self.value) diff --git a/module/plugins/hooks/XMPP.py b/module/plugins/hooks/XMPP.py new file mode 100644 index 000000000..1e0eda59b --- /dev/null +++ b/module/plugins/hooks/XMPP.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- + +import pyxmpp + +from pyxmpp.jabber.client import JabberClient + +from module.plugins.hooks.IRC import IRC + + +class XMPP(IRC, JabberClient): + __name__ = "XMPP" + __type__ = "hook" + __version__ = "0.15" + __status__ = "testing" + + __config__ = [("activated", "bool", "Activated" , False ), + ("jid" , "str" , "Jabber ID" , "user@exmaple-jabber-server.org" ), + ("pw" , "str" , "Password" , "" ), + ("tls" , "bool", "Use TLS" , False ), + ("owners" , "str" , "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), + ("info_file", "bool", "Inform about every file finished" , False ), + ("info_pack", "bool", "Inform about every package finished" , True ), + ("captcha" , "bool", "Send captcha requests" , True )] + + __description__ = """Connect to jabber and let owner perform different tasks""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + + + pyxmpp.interface.implements(IMessageHandlersProvider) + + + def __init__(self, *args, **kwargs): + IRC.__init__(self, *args, **kwargs) + + self.jid = pyxmpp.all.JID(self.config.get('jid')) + password = self.config.get('pw') + + #: If bare JID is provided add a resource -- it is required + if not self.jid.resource: + self.jid = pyxmpp.all.JID(self.jid.node, self.jid.domain, "pyLoad") + + if self.config.get('tls'): + tls_settings = pyxmpp.streamtls.TLSSettings(require=True, verify_peer=False) + auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") + else: + tls_settings = None + auth = ("sasl:DIGEST-MD5", "digest") + + #: Setup client with provided connection information + #: And identity data + JabberClient.__init__(self, self.jid, password, + disco_name="pyLoad XMPP Client", disco_type="bot", + tls_settings=tls_settings, auth_methods=auth) + + self.interface_providers = [ + VersionHandler(self), + self, + ] + + + def activate(self): + self.new_package = {} + + self.start() + + + def package_finished(self, pypack): + try: + if self.config.get('info_pack'): + self.announce(_("Package finished: %s") % pypack.name) + + except Exception: + pass + + + def download_finished(self, pyfile): + try: + if self.config.get('info_file'): + self.announce( + _("Download finished: %(name)s @ %(plugin)s") % {'name': pyfile.name, 'plugin': pyfile.pluginname}) + + except Exception: + pass + + + def run(self): + #: Connect to IRC etc. + self.connect() + try: + self.loop() + + except Exception, ex: + self.log_error(ex) + + + def stream_state_changed(self, state, arg): + """ + This one is called when the state of stream connecting the component + to a server changes. This will usually be used to let the user + know what is going on. + """ + self.log_debug("*** State changed: %s %r ***" % (state, arg)) + + + def disconnected(self): + self.log_debug("Client was disconnected") + + + def stream_closed(self, stream): + self.log_debug("Stream was closed", stream) + + + def stream_error(self, err): + self.log_debug("Stream Error", err) + + + def get_message_handlers(self): + """ + Return list of (message_type, message_handler) tuples. + + The handlers returned will be called when matching message is received + in a client session. + """ + return [("normal", self.message)] + + + def message(self, stanza): + """ + Message handler for the component. + """ + subject = stanza.get_subject() + body = stanza.get_body() + t = stanza.get_type() + self.log_debug("Message from %s received." % stanza.get_from()) + self.log_debug("Body: %s Subject: %s Type: %s" % (body, subject, t)) + + if t == "headline": + #: 'headline' messages should never be replied to + return True + if subject: + subject = u"Re: " + subject + + to_jid = stanza.get_from() + from_jid = stanza.get_to() + + # j = pyxmpp.all.JID() + to_name = to_jid.as_utf8() + from_name = from_jid.as_utf8() + + names = self.config.get('owners').split(";") + + if to_name in names or to_jid.node + "@" + to_jid.domain in names: + messages = [] + + trigger = "pass" + args = None + + try: + temp = body.split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + + except Exception: + pass + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + m = pyxmpp.all.Message( + to_jid=to_jid, + from_jid=from_jid, + stanza_type=stanza.get_type(), + subject=subject, + body=line) + + messages.append(m) + + except Exception, e: + self.log_error(e, trace=True) + + return messages + + else: + return True + + + def response(self, msg, origin=""): + return self.announce(msg) + + + def announce(self, message): + """ + Send message to all owners + """ + for user in self.config.get('owners').split(";"): + self.log_debug("Send message to", user) + + to_jid = pyxmpp.all.JID(user) + + m = pyxmpp.all.Message(from_jid=self.jid, + to_jid=to_jid, + stanza_type="chat", + body=message) + + stream = self.get_stream() + if not stream: + self.connect() + stream = self.get_stream() + + stream.send(m) + + + def before_reconnect(self, ip): + self.disconnect() + + + def after_reconnect(self, ip, oldip): + self.connect() + + +class VersionHandler(object): + """ + Provides handler for a version query. + + This class will answer version query and announce 'jabber:iq:version' namespace + in the client's disco#info results. + """ + pyxmpp.interface.implements(IIqHandlersProvider, IFeaturesProvider) + + + def __init__(self, client): + """ + Just remember who created this. + """ + self.client = client + + + def get_features(self): + """ + Return namespace which should the client include in its reply to a + disco#info query. + """ + return ["jabber:iq:version"] + + + def get_iq_get_handlers(self): + """ + Return list of tuples (element_name, namespace, handler) describing + handlers of stanzas + """ + return [("query", "jabber:iq:version", self.get_version)] + + + def get_iq_set_handlers(self): + """ + Return empty list, as this class provides no stanza handler. + """ + return [] + + + def get_version(self, iq): + """ + Handler for jabber:iq:version queries. + + jabber:iq:version queries are not supported directly by PyXMPP, so the + XML node is accessed directly through the libxml2 API. This should be + used very carefully! + """ + iq = iq.make_result_response() + q = iq.new_query("jabber:iq:version") + q.newTextChild(q.ns(), "name", "Echo component") + q.newTextChild(q.ns(), "version", "1.0") + return iq diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py deleted file mode 100644 index b8fe14239..000000000 --- a/module/plugins/hooks/XMPPInterface.py +++ /dev/null @@ -1,278 +0,0 @@ -# -*- coding: utf-8 -*- - -from pyxmpp import streamtls -from pyxmpp.all import JID, Message -from pyxmpp.interface import implements -from pyxmpp.interfaces import * -from pyxmpp.jabber.client import JabberClient - -from module.plugins.hooks.IRCInterface import IRCInterface - - -class XMPPInterface(IRCInterface, JabberClient): - __name__ = "XMPPInterface" - __type__ = "hook" - __version__ = "0.14" - __status__ = "testing" - - __config__ = [("activated", "bool", "Activated" , False ), - ("jid" , "str" , "Jabber ID" , "user@exmaple-jabber-server.org" ), - ("pw" , "str" , "Password" , "" ), - ("tls" , "bool", "Use TLS" , False ), - ("owners" , "str" , "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), - ("info_file", "bool", "Inform about every file finished" , False ), - ("info_pack", "bool", "Inform about every package finished" , True ), - ("captcha" , "bool", "Send captcha requests" , True )] - - __description__ = """Connect to jabber and let owner perform different tasks""" - __license__ = "GPLv3" - __authors__ = [("RaNaN", "RaNaN@pyload.org")] - - - implements(IMessageHandlersProvider) - - - def __init__(self, *args, **kwargs): - IRCInterface.__init__(self, *args, **kwargs) - - self.jid = JID(self.get_config('jid')) - password = self.get_config('pw') - - #: If bare JID is provided add a resource -- it is required - if not self.jid.resource: - self.jid = JID(self.jid.node, self.jid.domain, "pyLoad") - - if self.get_config('tls'): - tls_settings = streamtls.TLSSettings(require=True, verify_peer=False) - auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") - else: - tls_settings = None - auth = ("sasl:DIGEST-MD5", "digest") - - #: Setup client with provided connection information - #: And identity data - JabberClient.__init__(self, self.jid, password, - disco_name="pyLoad XMPP Client", disco_type="bot", - tls_settings=tls_settings, auth_methods=auth) - - self.interface_providers = [ - VersionHandler(self), - self, - ] - - - def activate(self): - self.new_package = {} - - self.start() - - - def package_finished(self, pypack): - try: - if self.get_config('info_pack'): - self.announce(_("Package finished: %s") % pypack.name) - - except Exception: - pass - - - def download_finished(self, pyfile): - try: - if self.get_config('info_file'): - self.announce( - _("Download finished: %(name)s @ %(plugin)s") % {'name': pyfile.name, 'plugin': pyfile.pluginname}) - - except Exception: - pass - - - def run(self): - #: Connect to IRC etc. - self.connect() - try: - self.loop() - - except Exception, ex: - self.log_error(ex) - - - def stream_state_changed(self, state, arg): - """ - This one is called when the state of stream connecting the component - to a server changes. This will usually be used to let the user - know what is going on. - """ - self.log_debug("*** State changed: %s %r ***" % (state, arg)) - - - def disconnected(self): - self.log_debug("Client was disconnected") - - - def stream_closed(self, stream): - self.log_debug("Stream was closed", stream) - - - def stream_error(self, err): - self.log_debug("Stream Error", err) - - - def get_message_handlers(self): - """ - Return list of (message_type, message_handler) tuples. - - The handlers returned will be called when matching message is received - in a client session. - """ - return [("normal", self.message)] - - - def message(self, stanza): - """ - Message handler for the component. - """ - subject = stanza.get_subject() - body = stanza.get_body() - t = stanza.get_type() - self.log_debug("Message from %s received." % stanza.get_from()) - self.log_debug("Body: %s Subject: %s Type: %s" % (body, subject, t)) - - if t == "headline": - #: 'headline' messages should never be replied to - return True - if subject: - subject = u"Re: " + subject - - to_jid = stanza.get_from() - from_jid = stanza.get_to() - - # j = JID() - to_name = to_jid.as_utf8() - from_name = from_jid.as_utf8() - - names = self.get_config('owners').split(";") - - if to_name in names or to_jid.node + "@" + to_jid.domain in names: - messages = [] - - trigger = "pass" - args = None - - try: - temp = body.split() - trigger = temp[0] - if len(temp) > 1: - args = temp[1:] - - except Exception: - pass - - handler = getattr(self, "event_%s" % trigger, self.event_pass) - try: - res = handler(args) - for line in res: - m = Message( - to_jid=to_jid, - from_jid=from_jid, - stanza_type=stanza.get_type(), - subject=subject, - body=line) - - messages.append(m) - - except Exception, e: - self.log_error(e, trace=True) - - return messages - - else: - return True - - - def response(self, msg, origin=""): - return self.announce(msg) - - - def announce(self, message): - """ - Send message to all owners - """ - for user in self.get_config('owners').split(";"): - self.log_debug("Send message to", user) - - to_jid = JID(user) - - m = Message(from_jid=self.jid, - to_jid=to_jid, - stanza_type="chat", - body=message) - - stream = self.get_stream() - if not stream: - self.connect() - stream = self.get_stream() - - stream.send(m) - - - def before_reconnect(self, ip): - self.disconnect() - - - def after_reconnect(self, ip, oldip): - self.connect() - - -class VersionHandler(object): - """ - Provides handler for a version query. - - This class will answer version query and announce 'jabber:iq:version' namespace - in the client's disco#info results. - """ - implements(IIqHandlersProvider, IFeaturesProvider) - - - def __init__(self, client): - """ - Just remember who created this. - """ - self.client = client - - - def get_features(self): - """ - Return namespace which should the client include in its reply to a - disco#info query. - """ - return ["jabber:iq:version"] - - - def get_iq_get_handlers(self): - """ - Return list of tuples (element_name, namespace, handler) describing - handlers of stanzas - """ - return [("query", "jabber:iq:version", self.get_version)] - - - def get_iq_set_handlers(self): - """ - Return empty list, as this class provides no stanza handler. - """ - return [] - - - def get_version(self, iq): - """ - Handler for jabber:iq:version queries. - - jabber:iq:version queries are not supported directly by PyXMPP, so the - XML node is accessed directly through the libxml2 API. This should be - used very carefully! - """ - iq = iq.make_result_response() - q = iq.new_query("jabber:iq:version") - q.newTextChild(q.ns(), "name", "Echo component") - q.newTextChild(q.ns(), "version", "1.0") - return iq -- cgit v1.2.3