# LocalWords: asyncio async plugin * Setting up the bot D'bolla is a no bullshit extensible IRC/Slack bot written for Python 3.5 using asyncio. ** Install dependencies: #+BEGIN_SRC bash pip install websockets #+END_SRC ** Configure Copy dbolla.conf.example to dbolla.conf and edit this file. You can specify multiples of the same connection by prefixing the config section with 'slack:' or 'irc:' followed by a unique name for this connection. ** Running Simply run the command: #+BEGIN_SRC bash ./bin/dbolla -c /path/to/my/dbolla.conf #+END_SRC * Writing a Plugin To write a new plugin you must inherit from ~warmachine.addons.base.WarMachinePlugin~. This class defines an interface you must implement in order to interact with connections. If you override ~__init__~ you must call ~super()~. ** ~self._loop~ This is given to you access to the asyncio loop. This is useful for scheduling tasks to be run later. ** ~self.config_dir~ The directory to store any configuration files and other junk in. ** ~self.log~ This is a logger that you should use when logging messages. ** ~self.recv_msg(channel, message)~ ~recv_msg~ is the only required method for a plugin. It is called for every plugin every time a connection receives a message. It takes the arguments ~connection~ and ~message~. ~connection~ is the Connection object which allows you to interact with that chat server. ~message~ is a dictionary containing information about the message received. It has the following format: #+BEGIN_SRC python { 'sender': 'sender nickname', 'channel': '#channel_name or None for private messages', 'message': 'The message that was received', } #+END_SRC You will parse ~message['message']~ for what you are interested in and take some action based on what was found. For example: #+BEGIN_SRC python async def recv_msg(connection, message): """ A simple echo 'server'. """ # if the message starts with !echo, and it was said in a channel (not a # private message)... if message['message'].startswith('!echo') and message['channel']: # Strip the command !echo from the message try: msg_to_repeat = ' '.join(message['message'].split(' ')[1:]) except IndexError: # There was nothing to echo return # Repeat the remainder of the received message await connection.say(msg_to_repeat, message['channel']) #+END_SRC ** ~self.on_connect(connection)~ This method is called when a connection successfully connects and takes the single argument ~connection~. Use this method to do any start up initialization you may want to do such as create configuration files that don't exist yet. * Writing a Connection To write a new connection protocol you must inherit from ~warmachine.connections.base.Connection~. This class defines an interface you must implement to support the plugins. If you override ~__init__~ you must call ~super()~. ** ~__config_prefix__~ This global is used to decide which connection to use when it is found in the config file. E.g. IRC uses ~'irc'~ and Slack uses ~'slack'~. It should be defined somewhere near the top of your file. ** ~self.config_dir~ The directory to store any configuration files and other junk in. ** ~self.connect()~ D'bolla uses python 3's asyncio to manage multiple connections concurrently thus you should use ~asyncio.open_connection~ to create your connection. Once you have successfully connected you must set ~self.status~ to ~warmachine.connections.base.CONNECTED~. This indicates the connection is ready to use. ** ~self.read()~ This method is constantly checked in a loop by the ~Bot~ class. When a message is returned it is passed into the ~recv_msg~ method in all loaded plugins. This return value should be formatted in the following format: #+BEGIN_SRC python { 'sender': 'sender nickname', 'channel': '#channel_name or None for private messages', 'message': 'The message that was received', } #+END_SRC ** ~self.say(message, destination)~ This method is used by plugins to send a message to a channel or user. ** ~self.id~ This should return a unique id used to identify this particular connection. This is used by plugins when saving state. As an example, the IRC connection uses something like this: #+BEGIN_SRC python @property @warmachine.utils.decorators.memoize def id(self): from hashlib import md5 value = '{}-{}'.format(self.host, self.nick) return md5(value.encode()).hexdigest() #+END_SRC ** ~self.get_users_by_channel(channel)~ This method should return a list of all users (including the bot) for the connection.