diff --git a/docs/api/modules/pubsubclient.rst b/docs/api/modules/pubsubclient.rst new file mode 100644 index 0000000..af5d285 --- /dev/null +++ b/docs/api/modules/pubsubclient.rst @@ -0,0 +1,139 @@ +:mod:`PubSubClient` --- Message bus based RPC +============================================= + + +This module connects to a pub/sub message bus and provices RPC functionality. +It broadcasts status messages and reacts to different messages. The client / +server for the message bus is http://gitlab.davepedu.com/dave/pymsgbus. Under +the hood, this is ZeroMQ. + +Config +------ + +.. code-block:: json + + { + "servers": ["127.0.0.1:7100"], + "subscriptions": ["pyircbot_send"], + "publish": "pyircbot_{}", + "name": "default" + } + +.. cmdoption:: servers + + Message bus server(s) to connect or fall back to. Currently, only use of + the first is implemented. + +.. cmdoption:: subscriptions + + List of bus channels to subscribe to + +.. cmdoption:: publish + + Publish channel name template. Will be formatted with the sub-type of + message, such as `privmsg`. + +.. cmdoption:: name + + Multiple bots can connect to one bus. They can be given a name to target a + single bot; all will respond to the `default` name. + +Bus Messages +------------ + +In the format of: + +.. code-block:: text + + + +Bot connects to the bus: + +.. code-block:: text + + pyircbot_sys default online + +Bot disconnects from the bus: + +.. code-block:: text + + pyircbot_sys default offline + +Bot relaying a privmsg: + +.. code-block:: text + + pyircbot_privmsg default [["#clonebot"], "dave-irccloud", "message text", + {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] + +User parts a channel: + +.. code-block:: text + + pyircbot_part default [["#clonebot"], "dave-irccloud", "part msg", + {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] + +User joins a channel: + +.. code-block:: text + + pyircbot_join default ["dave-irccloud", "#clonebot", + {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] + +User uses the command `.seen testbot`: + +.. code-block:: text + + pyircbot_command_seen default [["#clonebot"], "dave-irccloud", "testbot", + {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] + +Client sending a message that the bot will relay + +.. code-block:: text + + pyircbot_send default privmsg ["#clonebot", "asdf1234"] + +""" + +Example Programs +---------------- + +Sends the content of a text file line-by-line with a delay: + +.. code-block:: python + + from contextlib import closing + from msgbus.client import MsgbusSubClient + import argparse + from time import sleep + from json import dumps + + + def main(): + parser = argparse.ArgumentParser(description="send irc art") + parser.add_argument("-i", "--host", default="127.0.0.1", help="host to connect to") + parser.add_argument("-p", "--port", default=7003, help="port to connect to") + parser.add_argument("-c", "--channel", required=True, help="irc channel") + parser.add_argument("-f", "--file", required=True, help="file containing irc lines to send") + parser.add_argument("--delay", type=float, default=1.0, help="delay between lines (s)") + args = parser.parse_args() + + with open(args.file) as f: + with closing(MsgbusSubClient(args.host, args.port)) as client: + for line in f: + line = line.rstrip() + print(line) + client.pub("pyircbot_send", "default privmsg {}".format(dumps([args.channel, line]))) + sleep(args.delay) + + + if __name__ == '__main__': + main() + +Class Reference +--------------- + +.. automodule:: pyircbot.modules.PubSubClient + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/builder/Dockerfile b/docs/builder/Dockerfile index 7507697..089bd02 100644 --- a/docs/builder/Dockerfile +++ b/docs/builder/Dockerfile @@ -1,10 +1,17 @@ -FROM ubuntu:xenial +FROM ubuntu:artful RUN apt-get update ; \ apt-get install -y python3 python3-sphinx python3-setuptools python3-dev python3-requests python3-pip python3-lxml make wget unzip libmysqlclient-dev RUN pip3 install pytz praw releases +RUN cd /tmp ; \ + wget -O msgbus.tar.gz 'http://gitlab.davepedu.com/dave/pymsgbus/repository/archive.tar.gz?ref=master' && \ + mkdir pymsgbus && tar zxvf msgbus.tar.gz --strip-components 1 -C pymsgbus/ &&\ + cd pymsgbus && \ + pip3 install -r requirements.txt && \ + python3 setup.py install + RUN cd /tmp ; \ wget https://github.com/jgarzik/python-bitcoinrpc/archive/master.zip ; \ unzip master.zip ; \ diff --git a/docs/setup/dependencies.rst b/docs/setup/dependencies.rst index 2d6eb64..328b28b 100644 --- a/docs/setup/dependencies.rst +++ b/docs/setup/dependencies.rst @@ -2,7 +2,7 @@ Dependencies ************ -PyIRCBot is designed to run on Python 3.5+, and is usually tested with 3.5. +PyIRCBot is designed to run on Python 3.6+, and is usually tested with 3.6. Python 2.x and older versions of 3.x are not supported. Although **no** non-core modules are needed to run PyIRCBot in it's most basic @@ -23,6 +23,7 @@ them. - **bitcoinrpc** - https://github.com/jgarzik/python-bitcoinrpc - **pymysql** - https://github.com/dpedu/MySQL-for-Python-3 (needs \ libmysqlclient-dev on your system) +- **pymsgbus** - http://gitlab.davepedu.com/dave/pymsgbus At time of writing there is a bug that will prevent the bitcoinrpc module from working with Python 3. When pull `#55`_ is merged, the bug will be fixed. diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile index eb521c4..9d435a9 100644 --- a/examples/docker/Dockerfile +++ b/examples/docker/Dockerfile @@ -1,15 +1,20 @@ -FROM ubuntu:xenial +FROM ubuntu:artful ENTRYPOINT ["/usr/local/bin/pyircbot"] WORKDIR /srv/bot/ CMD ["-c", "config.json"] -RUN sed -i -e 's/archive.ubuntu.com/mirrors.digitalocean.com/' /etc/apt/sources.list && \ - apt-get update && \ +RUN apt-get update && \ apt-get install -y python3 python3-setuptools python3-requests curl unzip sqlite3 && \ easy_install3 pip && \ pip3 install praw==5.0.1 pytz && \ cd /tmp && \ + curl -o msgbus.tar.gz 'http://gitlab.davepedu.com/dave/pymsgbus/repository/archive.tar.gz?ref=master' && \ + mkdir pymsgbus && tar zxvf msgbus.tar.gz --strip-components 1 -C pymsgbus/ &&\ + cd pymsgbus && \ + pip3 install -r requirements.txt && \ + python3 setup.py install && \ + cd /tmp && \ curl -o bitcoinrpc.tar.gz https://codeload.github.com/dpedu/python-bitcoinrpc/tar.gz/master && \ tar zxvf bitcoinrpc.tar.gz && \ cd python-bitcoinrpc-master && \ diff --git a/pyircbot/modules/PubSubClient.py b/pyircbot/modules/PubSubClient.py index 744d9fb..9246adc 100644 --- a/pyircbot/modules/PubSubClient.py +++ b/pyircbot/modules/PubSubClient.py @@ -27,6 +27,9 @@ class PubSubClient(ModuleBase): self.bus_listener_thread.start() def bus_listener(self): + """ + Listen to the bus for send messages and act on recv + """ sleep(3) while True:#TODO clean exit onenable/ondisable etc if not self.bus: @@ -39,10 +42,9 @@ class PubSubClient(ModuleBase): continue try: print(channel, "--", message) - tag, subcommand, message = message.split(" ", 2) - if tag != "default": + name, subcommand, message = message.split(" ", 2) + if name != self.config.get("name", "default") and name != "default": continue - if subcommand == "privmsg": dest, message = loads(message) self.bot.act_PRIVMSG(dest, message) @@ -50,27 +52,42 @@ class PubSubClient(ModuleBase): print_exc() def publish(self, subchannel, message): + """ + Abstracted callback for proxying irc messages to the bs + :param subchannel: event type such as "privmsg" + :type subchannel: str + :param message: message body + :type message: str + """ self.bus.pub(self.config.get("publish").format(subchannel), "{} {}".format("default", message)) @hook("PRIVMSG") def bus_privmsg(self, msg): - # msg.command msg.args msg.prefix msg.trailing + """ + Relay a privmsg to the event bus + """ self.publish("privmsg", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}])) @hook("JOIN") def bus_join(self, msg): - # msg.command msg.args msg.prefix msg.trailing + """ + Relay a join message to the event bus + """ self.publish("join", dumps([msg.prefix[0], msg.trailing, {"prefix": msg.prefix}])) @hook("PART") def bus_part(self, msg): - # msg.command msg.args msg.prefix msg.trailing + """ + Relay a part message to the event bus + """ self.publish("part", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}])) @hook("PRIVMSG") def bus_command(self, msg): - # msg.command msg.args msg.prefix msg.trailing - # self.publish("privmsg", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}])) + """ + Parse commands and publish as separate channels on the bus. Commands like `.seen nick` will be published + to channel `command_seen`. + """ match = COMMAND_RE.match(msg.trailing) if match: cmd_name = match.groups()[1] @@ -79,48 +96,18 @@ class PubSubClient(ModuleBase): dumps([msg.args, msg.prefix[0], cmd_args, {"prefix": msg.prefix}])) def onenable(self): + """ + Connect to the message bus when the module is enabled + """ self.bus = MsgbusSubClient(self.host, int(self.port)) for channel in self.config.get("subscriptions"): self.bus.sub(channel) self.publish("sys", "online") def ondisable(self): + """ + Disconnect to the message bus on shutdown + """ self.log.warning("clean it up") self.publish("sys", "offline") self.bus.close() - -""" - -Bot connects to the bus: - - pyircbot_sys default online - -Bot disconnects from the bus: - - pyircbot_sys default offline - -Bot relaying a privmsg: - - pyircbot_privmsg default [["#clonebot"], "dave-irccloud", "message text", - {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] - -User parts a channel: - - pyircbot_part default [["#clonebot"], "dave-irccloud", "part msg", - {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] - -User joins a channel: - - pyircbot_join default ["dave-irccloud", "#clonebot", - {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] - -User uses the command `.seen testbot`: - - pyircbot_command_seen default [["#clonebot"], "dave-irccloud", "testbot", - {"prefix": ["dave-irccloud", "sid36094", "Clk-247B1F43.irccloud.com"]}] - -# Client sending a message that the bot will relay - - pyircbot_send default privmsg ["#clonebot", "asdf1234"] - -"""