commit 9996d8665d68047fcdfed2b0e0ee9a9569eac21e Author: mb@mb Date: Tue Mar 6 19:39:30 2018 +0100 first commit! diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb0a672 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +This is the home of Varia's xmpp.streambot! + +When the streambot is listening ... it saves all images that are sent to the muc members@vvvvvvaria.org and streams them to [vvvvvvaria.org/stream/](https://vvvvvvaria.org/stream/). + +*archivist.py* was written by Ruben van der Ven during Relearn 2017. + +*streambot.py* is based on *archivist.py*, it is a custom Varia bot, that will communicate one day with our Pelican plugins-custom/stream plugin. :) diff --git a/archivist.py b/archivist.py new file mode 100644 index 0000000..68704c1 --- /dev/null +++ b/archivist.py @@ -0,0 +1,141 @@ +import logging + +import argparse +from sleekxmpp import ClientXMPP +from sleekxmpp.exceptions import IqError, IqTimeout +import os +import urlparse +import urllib +import datetime +import ssl +import base64 +from PIL import Image +import cStringIO + +parser = argparse.ArgumentParser() +parser.add_argument("-j", "--jid", help="jabber identifier", type=str, required=True) +parser.add_argument("-p", "--password", help="password", type=str, required=True) +parser.add_argument("-m", "--muc", help="destination muc", type=str, required=True) +parser.add_argument("-n", "--nick", help="nickname of the bot", default="archivist", type=str) +args = parser.parse_args() + +class ArchivistBot(ClientXMPP): + + def __init__(self, jid, password, room, nick): + ClientXMPP.__init__(self, jid, password) + + self.dbfile = 'archive.db' + self.datadir = 'files/' + + self.room = room + self.nick = nick + + self.add_event_handler("session_start", self.session_start) + self.add_event_handler("message", self.archive_msg) # by using 'message' instead of 'groupchat_message' every message received can be archived (also personal msgs) + + # self.add_event_handler("groupchat_message", self.archive_msg2) + + self.register_plugin('xep_0045') + self.register_plugin('xep_0030') + self.register_plugin('xep_0084') + self.register_plugin('xep_0096') # Transfer files.. + self.register_plugin('xep_0066') # Transfer files.. + + def session_start(self, event): + self.get_roster() + self.send_presence() + self.plugin['xep_0045'].joinMUC(self.room, self.nick) + + # XEP-0084 User Avatar + # Requires SleekXMPP 81b7b2c1908e0f6a5435ce67745b5f4dafb59816 + + with open('contact.png', 'rb') as avatar_file: + avatar = avatar_file.read() + avatar_id = self['xep_0084'].generate_id(avatar) + info = { + 'id': avatar_id, + 'type': 'image/jpeg', + 'bytes': len(avatar) + } + self['xep_0084'].publish_avatar(avatar) + self['xep_0084'].publish_avatar_metadata(items=[info]) + + # XEP-0153: vCard-Based Avatars + # Not working ATM + + self['xep_0153'].set_avatar(avatar=avatar, mtype='image/png') + + def archive_msg(self, msg): + # Always check that a message is not from yourself, otherwise you will create an infinite loop responding to your own messages. + if 'mucnick' in msg and msg['mucnick'] == self.nick: + return + + # in case of a whisper, nick becomes archive@muc.complex.local/ccl + nick = msg['mucnick'] if len(msg['mucnick']) else msg['from'] + + nowDate = datetime.datetime.now().strftime("%Y-%m-%d") + targetHtmlFile = os.path.join(self.datadir, nowDate+".html") + + if not os.path.exists(targetHtmlFile): + with open(targetHtmlFile, "wb") as f: + f.write("Archive of %s at %s" % (nowDate, self.room)) + + now = datetime.datetime.now().isoformat() + content = "
%s%s" % (now, nick) + + if msg['oob']['url']: + logging.getLogger().debug("received OOB from %s with %s" % (nick, msg['oob']['url'])) + filename, ext = os.path.splitext(urlparse.urlparse(msg['oob']['url']).path) + targetDir = os.path.join(self.datadir, nowDate) + if not os.path.exists(targetDir): + os.mkdir( targetDir, 0755 ) + + filename = now +"_" + nick + ext + targetFile = os.path.join(targetDir, filename) + targetThumbFile = os.path.join(targetDir, "thumb_"+filename) + targetUrl = os.path.join(nowDate, filename) + targetThumbUrl = os.path.join(nowDate, "thumb_"+filename) + #needed to disable certificate validation: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + urllib.urlretrieve(msg['oob']['url'], targetFile, context=ctx) + logging.getLogger().debug("saved to %s" % targetFile) + + size = (150,150) + img = Image.open(targetFile) + img.thumbnail(size) + buffer = cStringIO.StringIO() + img.save(buffer, format="JPEG") + img_str = base64.b64encode(buffer.getvalue()) + + content += "" % (targetUrl, img_str) + else: + logging.getLogger().debug("received text from %s: %s" % (nick, msg['body']) ) + content += "%s" % (msg['body']) + + content += "
" + + with open(targetHtmlFile, "a") as f: + f.write(content) + + + # if msg['type'] in ('chat', 'normal'): + # msg.reply("Thanks for sending\n%(body)s" % msg).send() + + def parseHtml(): + return + + +if __name__ == '__main__': + # Ideally use optparse or argparse to get JID, + # password, and log level. + + logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s') + + client = ArchivistBot(args.jid, args.password, args.muc, args.nick) + + if client.connect(): + client.process(block=True) + else: + logging.getLogger().error("Can't connect.") diff --git a/contact.png b/contact.png new file mode 100644 index 0000000..c01a169 Binary files /dev/null and b/contact.png differ diff --git a/files/SpYFTy_nQyyxcNkUKniWdw.jpg b/files/SpYFTy_nQyyxcNkUKniWdw.jpg new file mode 100644 index 0000000..95a2787 Binary files /dev/null and b/files/SpYFTy_nQyyxcNkUKniWdw.jpg differ diff --git a/files/jyTSOwDEQCaqKHnDdHeNow.jpg b/files/jyTSOwDEQCaqKHnDdHeNow.jpg new file mode 100644 index 0000000..e303c29 Binary files /dev/null and b/files/jyTSOwDEQCaqKHnDdHeNow.jpg differ diff --git a/streambot.py b/streambot.py new file mode 100644 index 0000000..ac16f23 --- /dev/null +++ b/streambot.py @@ -0,0 +1,103 @@ +import logging + +import argparse +from sleekxmpp import ClientXMPP +from sleekxmpp.exceptions import IqError, IqTimeout +import os +import urllib +import datetime +import ssl +from PIL import Image + +parser = argparse.ArgumentParser() +parser.add_argument("-j", "--jid", help="jabber identifier", type=str, required=True) +parser.add_argument("-p", "--password", help="password", type=str, required=True) +parser.add_argument("-m", "--muc", help="destination muc", type=str, required=True) +parser.add_argument("-n", "--nick", help="nickname of the bot", default="archivist", type=str) +args = parser.parse_args() + +class ArchivistBot(ClientXMPP): + + def __init__(self, jid, password, room, nick): + ClientXMPP.__init__(self, jid, password) + + self.datadir = 'files/' + + self.room = room + self.nick = nick + + self.add_event_handler("session_start", self.session_start) + self.add_event_handler("message", self.archive_msg) # by using 'message' instead of 'groupchat_message' every message received can be archived (also personal msgs) + + # self.add_event_handler("groupchat_message", self.archive_msg2) + + self.register_plugin('xep_0045') + self.register_plugin('xep_0030') + self.register_plugin('xep_0084') + self.register_plugin('xep_0096') # Transfer files.. + self.register_plugin('xep_0066') # Transfer files.. + + def session_start(self, event): + self.get_roster() + self.send_presence() + self.plugin['xep_0045'].joinMUC(self.room, self.nick) + + # XEP-0084 User Avatar + # Requires SleekXMPP 81b7b2c1908e0f6a5435ce67745b5f4dafb59816 + + with open('contact.png', 'rb') as avatar_file: + avatar = avatar_file.read() + avatar_id = self['xep_0084'].generate_id(avatar) + info = { + 'id': avatar_id, + 'type': 'image/jpeg', + 'bytes': len(avatar) + } + self['xep_0084'].publish_avatar(avatar) + self['xep_0084'].publish_avatar_metadata(items=[info]) + + # XEP-0153: vCard-Based Avatars + # Not working ATM + + self['xep_0153'].set_avatar(avatar=avatar, mtype='image/png') + + def archive_msg(self, msg): + # Always check that a message is not from yourself, otherwise you will create an infinite loop responding to your own messages. + if 'mucnick' in msg and msg['mucnick'] == self.nick: + return + + if msg['oob']['url']: + logging.getLogger().debug("received OOB from %s with %s" % (nick, msg['oob']['url'])) + + filename = os.path.basename(msg['oob']['url']) + targetDir = self.datadir + if not os.path.exists(targetDir): + os.mkdir( targetDir, 0755 ) + + targetFile = os.path.join(targetDir, filename) + + #needed to disable certificate validation: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + # save image to file + urllib.urlretrieve(msg['oob']['url'], targetFile, context=ctx) + logging.getLogger().debug("saved to %s" % targetFile) + + # if msg['type'] in ('chat', 'normal'): + # msg.reply("Thanks for sending\n%(body)s" % msg).send() + + +if __name__ == '__main__': + # Ideally use optparse or argparse to get JID, + # password, and log level. + + logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s') + + client = ArchivistBot(args.jid, args.password, args.muc, args.nick) + + if client.connect(): + client.process(block=True) + else: + logging.getLogger().error("Can't connect.")