diff --git a/README.md b/README.md index 8df9455..eb6cca6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # hptoad -An MIT licensed XMPP bot written using Python and sleekxmpp. +An MIT licensed XMPP bot written using Python 3 and slixmpp. diff --git a/gsend.py b/gsend.py index 4428570..be7d428 100755 --- a/gsend.py +++ b/gsend.py @@ -1,8 +1,7 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 import sys -import sleekxmpp +import slixmpp opts = { "jid": "botname@example.com", @@ -12,9 +11,14 @@ opts = { } +def on_failed_all_auth(event): + print("Auth: Could not connect to the server, or password mismatch!") + sys.exit(1) + + def on_session_start(event): - client.get_roster() # client.send_presence() + client.get_roster() body = "\n".join(sys.argv[1:]).strip() try: @@ -23,20 +27,20 @@ def on_session_start(event): except Exception as e: print("%s: %s" % (type(e).__name__, str(e))) finally: - client.disconnect(wait=True) - sys.exit(0) + client.disconnect() + + +def on_session_end(event): + sys.exit(0) if __name__ == "__main__": - if sys.version_info.major < 3: - sleekxmpp.util.misc_ops.setdefaultencoding("utf-8") - if len(sys.argv) <= 1: print("At least one argument is required.") sys.exit(1) - client = sleekxmpp.ClientXMPP("%s/%s" % (opts["jid"], opts["resource"]), - opts["password"]) + client = slixmpp.ClientXMPP("%s/%s" % (opts["jid"], opts["resource"]), + opts["password"]) if opts["connect"]: connect = opts["connect"].split(":", 1) @@ -47,9 +51,8 @@ if __name__ == "__main__": else: connect = () - if client.connect(connect): - client.add_event_handler("session_start", on_session_start) - client.process(block=True) - else: - print("Could not connect to server, or password mismatch!") - sys.exit(1) + client.connect(connect) + client.add_event_handler("failed_all_auth", on_failed_all_auth) + client.add_event_handler("session_start", on_session_start) + client.add_event_handler("session_end", on_session_end) + client.process(forever=True) diff --git a/hptoad.py b/hptoad.py index 530333f..5a15e4b 100755 --- a/hptoad.py +++ b/hptoad.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +import asyncio import logging import os import re @@ -8,7 +8,7 @@ import signal import subprocess import sys import time -import sleekxmpp +import slixmpp opts = { "muc": "room@conference.example.com", @@ -22,12 +22,9 @@ opts = { class Hptoad: def __init__(self, opts): - if sys.version_info.major < 3: - sleekxmpp.util.misc_ops.setdefaultencoding("utf-8") - - self.client = sleekxmpp.ClientXMPP("%s/%s" % (opts["jid"], - opts["resource"]), - opts["password"]) + self.client = slixmpp.ClientXMPP("%s/%s" % (opts["jid"], + opts["resource"]), + opts["password"]) self.client.register_plugin("xep_0199") # XMPP Ping. self.client.register_plugin("xep_0045") # XMPP MUC. self.muc_obj = self.client.plugin["xep_0045"] @@ -43,10 +40,11 @@ class Hptoad: self.bot_nick = self.pure_bot_nick def register_handlers(self): + self.client.add_event_handler("failed_all_auth", + self.on_failed_all_auth) self.client.add_event_handler("session_start", self.on_session_start) self.client.add_event_handler("session_end", self.on_session_end) - self.client.add_event_handler("message", self.on_message, - threaded=True) + self.client.add_event_handler("message", self.on_message) self.client.add_event_handler("muc::%s::presence" % self.muc, self.on_muc_presence) @@ -63,13 +61,10 @@ class Hptoad: else: connect = () - if not self.client.connect(connect): - self.logger.critical("Auth: Could not connect to server, or " + - "password mismatch!") - sys.exit(1) + self.client.connect(connect) def join_muc(self): - self.muc_obj.joinMUC(self.muc, self.bot_nick) + self.muc_obj.join_muc(self.muc, self.bot_nick) def log_exception(self, ex): self.logger.error("%s: %s" % (type(ex).__name__, str(ex))) @@ -83,7 +78,7 @@ class Hptoad: if nick not in self.muc_obj.rooms[self.muc]: return False - affiliation = self.muc_obj.getJidProperty(muc, nick, "affiliation") + affiliation = self.muc_obj.get_jid_property(muc, nick, "affiliation") return True if affiliation in ("admin", "owner") else False _trim_regexp = re.compile("(`|\\$|\\.\\.)") @@ -98,6 +93,7 @@ class Hptoad: # letter(ASCII or cyrillic), number, underscore only. _cmd_validator_regexp = re.compile("^!(\\w|\\p{Cyrillic})*$") + @asyncio.coroutine def prep_extern_cmd(self, body, nick, dir_path, is_admin=False): cmd = body.split(" ", 1) cmd[0] = cmd[0].strip() @@ -122,12 +118,13 @@ class Hptoad: return proc_args, None + @asyncio.coroutine def extern_cmd(self, body, nick, from_id, dir_path, is_admin=False): reply = "" err = None - cmd, prep_err = self.prep_extern_cmd(body, nick, dir_path, - is_admin=is_admin) + cmd, prep_err = yield from self.prep_extern_cmd(body, nick, dir_path, + is_admin=is_admin) if prep_err: reply = "%s: WAT" % nick @@ -135,11 +132,11 @@ class Hptoad: return reply, err try: - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - cmd_reply, cmd_err = proc.communicate() + proc = yield from \ + asyncio.create_subprocess_exec(*cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + cmd_reply, cmd_err = yield from proc.communicate() except subprocess.CalledProcessError as e: reply = "%s: WAT" % nick err = "Execute: %s" % str(e) @@ -148,9 +145,10 @@ class Hptoad: if cmd_err and len(cmd_err.strip()) > 0: err = "Process: %s" % cmd_err.strip() if cmd_reply and len(cmd_reply.strip()) > 0: - reply = cmd_reply.strip() + reply = cmd_reply.decode().strip() return reply, err + @asyncio.coroutine def handle_cmd(self, body, nick, from_id, is_admin=False): reply = "" err = None @@ -174,14 +172,17 @@ class Hptoad: reply = "%s: GTFO" % nick elif body.startswith("!"): # Any external command. - reply, err = self.extern_cmd(body, nick, from_id, "plugins", - is_admin=is_admin) + reply, err = yield from self.extern_cmd(body, nick, + from_id, "plugins", + is_admin=is_admin) return reply, err + @asyncio.coroutine def handle_self_message(self, body, nick, from_id): if body.startswith("!"): - msg, err = self.handle_cmd(body, nick, from_id, is_admin=True) + msg, err = yield from self.handle_cmd(body, nick, from_id, + is_admin=True) else: msg = body.strip() @@ -189,6 +190,7 @@ class Hptoad: self.client.send_message(mto=self.muc, mbody=msg, mtype="groupchat") + @asyncio.coroutine def handle_muc_message(self, body, nick, from_id): is_admin = self.is_muc_admin(self.muc, nick) @@ -199,13 +201,13 @@ class Hptoad: call_regexp = re.compile("^%s[:,]" % self.bot_nick) if body.startswith("!"): # Any external command. - reply, err = self.handle_cmd(body, nick, from_id, - is_admin=is_admin) + reply, err = yield from self.handle_cmd(body, nick, from_id, + is_admin=is_admin) elif call_regexp.match(body): # Chat. cmd_body = call_regexp.sub("!answer", body) - reply, err = self.extern_cmd(cmd_body, nick, from_id, "chat", - is_admin=is_admin) + reply, err = yield from self.extern_cmd(cmd_body, nick, from_id, + "chat", is_admin=is_admin) if err: self.logger.error(err) @@ -215,16 +217,22 @@ class Hptoad: self.client.send_message(mto=self.muc, mbody=reply, mtype="groupchat") + def on_failed_all_auth(self, event): + self.logger.critical("Auth: Could not connect to the server, or " + + "password mismatch!") + sys.exit(1) + def on_session_start(self, event): - self.client.get_roster() self.client.send_presence(pstatus="is there some food in this world?", ppriority=12) + self.client.get_roster() self.join_muc() def on_session_end(self, event): time.sleep(2.0) self.connect() + @asyncio.coroutine def on_message(self, event): try: if not event["type"] in ("chat", "normal", "groupchat"): @@ -238,12 +246,12 @@ class Hptoad: if event["type"] == "groupchat": nick = event["mucnick"] if nick != self.bot_nick: - self.handle_muc_message(body, nick, from_id) + yield from self.handle_muc_message(body, nick, from_id) elif event["from"].bare == self.jid: # Use resource as a nickname with self messages. nick = from_id.resource - self.handle_self_message(body, nick, from_id) + yield from self.handle_self_message(body, nick, from_id) except Exception as e: self.log_exception(e) @@ -256,7 +264,7 @@ class Hptoad: if not typ: typ = event["type"] if not nick: - nick = self.muc_obj.getNick(self.muc, from_id) + nick = self.muc_obj.get_nick(self.muc, from_id) if typ == "error": if event["error"]["code"] == "409": @@ -274,7 +282,7 @@ class Hptoad: def run(self): self.register_handlers() self.connect() - self.client.process(block=True) + self.client.process(forever=True) if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index 86b3814..5593b66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -sleekxmpp>=1.2.0 +slixmpp