From 18bae6ec09c417005a438ce829746231c95b9d67 Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Sun, 10 Jan 2021 19:17:10 +0100 Subject: [PATCH] Refine API for replying --- CHANGELOG.md | 10 ++++++- README.md | 63 +++++++++++++++++++-------------------------- examples/echo.py | 5 ++-- examples/whisper.py | 6 ++--- pyproject.toml | 2 +- xbotlib.py | 30 ++++++++++++++------- 6 files changed, 63 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03495b6..94769e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,9 @@ -# xbotlib 0.1.0 (UNRELEASED) +# xbotlib x.x.x (UNRELEASED) + +# xbotlib 0.2.0 (2021-01-10) + +- Refine API for direct/chat responses + +# xbotlib 0.1.0 (2021-01-10) + +- Initial release diff --git a/README.md b/README.md index 46d7f5d..badb74a 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,15 @@ ## XMPP bots for humans -A friendly lightweight wrapper around [slixmpp](https://slixmpp.readthedocs.io/). +> status: experimental -`xbotlib` doesn't want to obscure the workings of the underlying library or -invent a totally new API. To this end, `xbotlib` is a [single file -implementation](./xbotlib.py) which can easily be understood and extended. It -provides a small API surface which reflects the `slixmpp` way of doing things. +A friendly lightweight wrapper around +[slixmpp](https://slixmpp.readthedocs.io/) for writing XMPP bots in Python. The +goal is to make writing and running XMPP bots easy and fun. -The goal is to make writing and running XMPP bots in Python easy and fun. +`xbotlib` is a [single file implementation](./xbotlib.py) which can easily be +understood and extended. It provides a small API surface which reflects the +`slixmpp` way of doing things. ## Install @@ -23,13 +24,16 @@ $ pip install xbotlib from xbotlib import Bot class EchoBot(Bot): - def reply_direct_chat(self, message): - self.send_direct_chat(to=message.sender, body=message.body) + def react(self, message): + if message.type == "chat": + self.reply(to=message.sender, body=message.body) MyBot() ``` -And then `python echo.py`. +And then `python echo.py`. You will be asked a few questions like which account +details your bot will be using. This will generate a `bot.conf` file in the +same working directory for further use. ## More Examples @@ -45,40 +49,25 @@ underling functions can be extended. For example, if you want to enable more plugins or add different functionality. If something feels awkwardthen please raise a ticket for that. Seamlessness is still a bitch but we're trying anyway. -### Bot.reply_direct_chat +### Bot.react A function which you define in your bot implementation in order to respond to -direct chat messages. +chat messages. You can respond to both direct messages and group chat messages +in this function by checking the `message.type` which can be either `chat` or +`groupchat`. Arguments: - **message**: sent message and metadata (see [message](#message) reference below) -### Bot.send_direct_chat +### Bot.reply Send back a response to a direct chat message. Arguments: -- **to**: who to send it to (can be a user or a room) -- **body**: the message to send - -### Bot.reply_group_chat - -A function which you define in your bot implementation in order to respond to -group chat messages. - -Arguments: - -- **message**: sent message and metadata (see [message](#message) reference below) - -### Bot.send_group_chat - -Send back a response to a group chat message. - -Arguments: - -- **to**: who to send it to (can be a user or a room) +- **to**: which user account to reply to (direct chat) +- **room**: which room to reply to (group chat) - **body**: the message to send ### Message @@ -87,11 +76,11 @@ A simple message format. Attributes: -- **body** -- **sender** -- **receive** -- **nickname** -- **type** +- **body**: the body of the message +- **sender**: the sender of the message +- **receive**: the receive of the message +- **nickname**: the nickname of the sender +- **type**: the type of message (`chat` or `groupchat`) ## Roadmap @@ -102,7 +91,7 @@ Attributes: - Extend the `bot.conf` to allow for multiple bot configurations. - Sort out something for how to deploy them. It's easy to run them locally but - hard to run them on server. Maybe something can be done for that as well. + hard to run them on a server. Maybe something can be done for that as well. ## Changes diff --git a/examples/echo.py b/examples/echo.py index 9b63c08..bd1e55b 100644 --- a/examples/echo.py +++ b/examples/echo.py @@ -8,9 +8,10 @@ class EchoBot(Bot): """ - def reply_direct_chat(self, message): + def react(self, message): """Send back what we get.""" - self.send_direct_chat(to=message.sender, body=message.body) + if message.type == "chat": + self.reply(to=message.sender, body=message.body) EchoBot() diff --git a/examples/whisper.py b/examples/whisper.py index 6b26900..e8bfe97 100644 --- a/examples/whisper.py +++ b/examples/whisper.py @@ -20,11 +20,11 @@ class WhisperBot(Bot): """ - def reply_direct_chat(self, message): + def react(self, message): """Receive direct messages and pass them to group chats.""" - if "whisper" in message.body: + if message.type == "chat" and "whisper" in message.body: _, room, whisper = message.body.split(":") - self.send_group_chat(to=room, body=f"*whispers* {whisper}") + self.reply(room=room, body=f"*whispers* {whisper}") WhisperBot() diff --git a/pyproject.toml b/pyproject.toml index 4d6aaac..8dc83e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api" [tool.poetry] name = "xbotlib" -version = "0.1.0" +version = "0.2.0" description = "XMPP bots for humans" authors = ["decentral1se "] maintainers = ["decentral1se "] diff --git a/xbotlib.py b/xbotlib.py index 44a5148..9906e97 100644 --- a/xbotlib.py +++ b/xbotlib.py @@ -61,7 +61,7 @@ class Bot(ClientXMPP): password = ( getpass("Password (e.g. my-cool-password): ") or "my-cool-password" ) - room = input("XMPP room (e.g. foo@bar.com): ") + room = input("XMPP room (e.g. foo@muc.bar.com): ") nick = input("Nickname (e.g. lurkbot): ") config = ConfigParser() @@ -90,7 +90,7 @@ class Bot(ClientXMPP): def message(self, message): """Handle message event.""" if message["type"] in ("chat", "normal"): - self.reply_direct_chat(EasyMessage(message)) + self.react(EasyMessage(message)) def session_start(self, event): """Handle session_start event.""" @@ -107,7 +107,7 @@ class Bot(ClientXMPP): """Handle groupchat_message event.""" if message["type"] in ("groupchat", "normal"): if message["mucnick"] != self.config["bot"]["nick"]: - self.reply_group_chat(EasyMessage(message)) + self.react(EasyMessage(message)) def register_xmpp_plugins(self): """Register XMPP plugins that the bot supports.""" @@ -124,10 +124,22 @@ class Bot(ClientXMPP): except KeyboardInterrupt: pass - def send_direct_chat(self, to, body): - """Reply to a direct chat message.""" - self.send_message(mto=to, mbody=body, mtype="chat") + def reply(self, to=None, room=None, body=None): + """Send back a reply.""" + if to is None and room is None: + message = "`to` or `room` arguments required for `reply`" + raise RuntimeError(message) - def send_group_chat(self, to, body): - """Reply to a group chat message.""" - self.send_message(mto=to, mbody=body, mtype="groupchat") + if to is not None and room is not None: + message = "Cannot send to both `to` and `room` for `reply`" + raise RuntimeError(message) + + kwargs = {"mbody": body} + if to is not None: + kwargs["mto"] = to + kwargs["mtype"] = "chat" + else: + kwargs["mto"] = room + kwargs["mtype"] = "groupchat" + + self.send_message(**kwargs)