[PYTHON] Let's make a Backend plugin for Errbot

Introduction

I created a Backend plugin for Python's ChatBot tool Errbot, so I'll write down how to make it.

Since the latest version of Errbot only supports Python3 series, this document also uses Python3 series.

What is a Backend plugin?

A plugin that connects Errbot to a chat system (Slack, HipChat, etc.). Major chat systems such as Slack and HipChat have official Backend plugins. (So I don't think this document will be read unless there's a good deal ...)

I want the bot to talk about this! See http://errbot.io/en/latest/user_guide/plugin_development/index.html for how to create a normal plugin such as.

How to make

First, read the [Advanced] Backend development page as much as possible. It's not such a long document, but English ability, English ability.

Below is a brief explanation.

Basic structure

All Backend plugins inherit from the ʻErrBot class. Then, override the methods existing in this ʻErrBot class as much as necessary and proceed with the implementation. Since the ʻErrBot class also inherits the Backend` class, there are a lot of methods that need to be implemented unexpectedly.

Looking at the inherited class, there are many methods that describe NotImplementedError, and the developer implements them according to the function of the chat system that he / she wants to support.

identifier

All Backend plugins need to manage ** user identifiers ** and ** user identifiers ** in chat rooms. The build_identifier method is especially important and needs to return these identifier instances from a particular string.

Feel the usage of identifiers in the following template without worrying about the details.

Model

Let's make a Backend plugin for the chat system called hoge.

It's been a long time to read on Qiita! For those who say, Github is also available. A setting example of config.py is also included. https://github.com/deko2369/errbot-backend-plugin-template

# -*- coding: utf-8 -*-
from errbot.backends.base import Message, ONLINE, Room, \
                                    RoomOccupant, Identifier
from errbot.core import ErrBot

# Can't use __name__ because of Yapsy
log = logging.getLogger('errbot.backends.hoge')

#Load the Hoge chat system library(Library does not exist)
try:
    import HogeChatClient
except ImportError:
    log.exception("Could not start the hoge backend")
    log.fatal(
        "You need to install the HogeChatClient package in order "
        "to use the Hoge backend. "
        "You should be able to install this package using: "
        "pip install HogeChatClient"
    )
    sys.exit(1)

class HogeUser(Identifier):
    """
A class that represents a user of a chat system

Build the Backend class without instantiating this class directly_In the identifier method
Create an object

    ref. http://errbot.io/en/latest/errbot.backends.base.html#errbot.backends.base.Identifier
    """
    def __init__(self, username, bot):
        """
Initialize user

username specifies the name used in the chat system
        """
        self._username = username
        self._bot = bot

    @property
    def username(self):
        """
Returns the user's name
        """
        self return._username

class HogeRoomOccupant(RoomOccupant, HogeUser):
    """
A class that represents a user in a chat system chat room

    ref. http://errbot.io/en/latest/errbot.backends.base.html#errbot.backends.base.RoomOccupant
    """
    def __init__(self, username, roomname, bot):
        """
Initialize users in a particular chat room
        """
        super().__init__(username, bot)
        self._room = HogeRoom(roomname, bot)

    @property
    def room(self):
        """
Returns room information
        """
        return self._room

class HogeBackend(ErrBot):
    """
Backend class body

I will write the actual interaction of the chat system here
    """
    def __init__(self, config):
        """
Initial setting
        """
        super().__init__(config)
        identity = config.BOT_IDENTIY
        self.token = identity.get('token', None)
        if not self.token:
            log.fatal(
                'You need to set your token in the BOT_IDENTITY setting '
                'in your config.py .'
            )
            sys.exit(1)

        #Initialize client by specifying token
        self.client = HogeChatClient.Client(self.token)

        #Create an identifier for the bot itself
        self.bot_identifier = HogeUser('BOT NAME', self)

    def build_reply(self, mess, text=None, private=False):
        """
Compose a reply message
        """
        #Build a message
        response = self.build_message(text)
        #Reply Source Identifier
        response.frm = self.bot_identifier
        #Reply to Identifier
        response.to = mess.frm

        return response

    def prefix_groupchat_reply(self, message, identifier):
        """
Template of reply message to group chat
        """
        message.body = '@%s %s' % (identifier.username, message.text)

    def build_message(self, text):
        """
Creating a message object
        """
        return super().build_message(text)

    def build_identifier(self, text_repf):
        """
Creating an Identifier object

Hoge chat system builds Identifier in the following format
user: @<username>
Users in chat rooms: @<username>#<roomname>
        """
        text = text_repr.strip()

        if text.startswith('@') and '#' not in text:
            return HogeUser(text.split('@')[1], self)
        elif '#' in text:
            username, roomname = text.split('#')
            return HogeRoomOccupant(username.split('@')[1], roomname, self)

        raise RuntimeError('Unrecognized identifier: %s' % text)

    def serve_once(self):
        """
Method to receive new message from chat system and process

This serve_The once method is called regularly by ErrBot
Serve to a similar overridden method_There is also forever

        ref. http://errbot.io/en/latest/errbot.backends.base.html#errbot.backends.base.Backend.serve_forever
        """
        #Get new messages
        mess = self.client.new_messages()

        #Process the acquired messages in order
        #The message also contains the username and the room name spoken
        for msg in mess:
            #Build a Message object
            m = Message(msg)
            m.frm = HogeRoomOccupant(msg.username, msg.roomname, self)
            m.to = HogeRoom(msg.roomname, self)

            #Call the message callback
            self.callback_message(m)

            # @<username>Callback if contains_Also call mention
            #Note that detailed implementation is omitted
            if '@' in msg:
                mentions = [HogeUser(username, self), ...]
                self.callback_mention(m, mentions)

    def send_message(self, mess):
        """
Implementation of the send part to the chat system
        """
        self.client.send(mess.body)

    def connect(self):
        """
Return the chat system library connection
        """
        return self.client

    def query_room(self, room)
        """
Process to return HogeRoom object from room string
        """
        r = self.client.room_info(room)
        return HogeRoom(r.name, self)

    @property
    def mode(self):
        """
A unique string that indicates the current backend
        """
        return 'hoge'

    @property
    def rooms(self):
        """
Return the Room instance that the bot is in
        """
        return []

    def change_presense(self, status=ONLINE, message=''):
        """
Process called when the bot's entry status changes
        """
        super().change_presence(status=status, message=message)

class HogeRoom(Room):
    """
Definition of a chat room in the Hoge chat system

Implement the ability for bots to join and create chat rooms
Impossible to implement if the chat system client does not provide such functionality...

    ref. http://errbot.io/en/latest/errbot.backends.base.html#errbot.backends.base.Room
    """
    def __init__(self, name, bot):
        self._name = name
        self._bot = bot

    def join(self, username=None, password=None):
        """
bot joins room
        """
        self._bot.client.join_room(self._name)

    def leave(self, reason=None):
        """
bot leaves from room
        """
        self._bot.client.leave_room(self._name)

    def create(self):
        """
bot creates a room
        """
        self._bot.client.create_room(self._name)

    def destroy(self):
        """
bot destroys a room
        """
        self._bot.client.destroy_room(self._name)

    @property
    def exists(self):
        """
Whether the room exists
        """
        return self._bot.client.exist_room(self._name)

    @property
    def joined(self):
        """
Whether the bot is joining the room
        """
        return self._bot.client.is_joined(self._name)

    @property
    def topic(self):
        """
Get room topic
        """
        return self._bot.client.room_info(self._name).topic

    @topic.setter
    def topic(self, topic):
        """
Set room topic
        """
        return self._bot.client.set_room_topic(self._name, topic)

    @property
    def occupants(self):
        """
Get the Identifier of the user who exists in the room
        """
        return [HogeUser(name, self._bot) \
                for name in self._bot.client.get_room_usernames(self._name)]

    def invite(self, *args):
        """
Invite users to the room
        """
        for ident in args:
            self._bot.client.invite(self._name, ident.username)

Finally

The side using Errbot is easy, but the developer who creates a bridge (Backend) with each chat system has a hard time w

reference

Recommended Posts

Let's make a Backend plugin for Errbot
Let's make an Errbot plugin
Let's make a module for Python using SWIG
Let's make a Discord Bot.
Make a Blueqat backend ~ Part 2
[For play] Let's make Yubaba a LINE Bot (Python)
Let's make a rock-paper-scissors game
Let's make a remote rumba [Hardware]
Let's make a remote rumba [Software]
Make a Tweet box for Pepper
Let's make a GUI with python.
Let's make a spot sale service 2
Let's make a breakout with wxPython
[Blender] How to make a Blender plugin
Let's make a graph with python! !!
Let's make a supercomputer with xCAT
Let's make a spot sale service 3
Let's make a WEB application for phone book with flask Part 1
Let's make a WEB application for phone book with flask Part 2
Let's make a WEB application for phone book with flask Part 3
Let's make a WEB application for phone book with flask Part 4
Let's make a shiritori game with Python
Let's create a virtual environment for Python
Let's make a voice slowly with Python
Let's make a simple language with PLY 1
Make Qt for Python app a desktop app
Let's make a multilingual site using flask-babel
ROS course 107 Make a client for rosblidge
Let's make a web framework with Python! (1)
Let's make a tic-tac-toe AI with Pylearn 2
Let's make a combination calculation in Python
Make a chessboard pattern for camera calibration
Let's make a Twitter Bot with Python!
Let's make a web framework with Python! (2)
How to make Spigot plugin (for Java beginners)
Make a bot for Skype on EC2 (CentOS)
How to make a QGIS plugin (package generation)
Let's replace UWSC with Python (5) Let's make a Robot
Make for VB6.
Let's make a spot sale service 9 (Task Queue edition)
I made a user management tool for Let's Chat
[Let's play with Python] Make a household account book
Let's make a simple game with Python 3 and iPhone
Let's make dependency management with pip a little easier
Let's make a Mac app with Tkinter and py2app
Let's make a spherical grid with Rhinoceros / Grasshopper / GHPython
Let's make a spot sale service 8 (image uploader edition)
[Super easy] Let's make a LINE BOT with Python.
Let's make a cron program in Java! !! (Task Scheduler)
Make a squash game
Make a function decorator
Make a distance matrix
Make a Nyan button
Make a Tetris-style game!
Make a Base64 decoder
Let's make a websocket client with Python. (Access token authentication)
Make an IP address allocation / allocation list for a certain area
Make a tky2jgd plugin with no practicality in QGIS Part 2
Yes, let's make a Minecraft server (Oracle Linux + Spigot + Geyser)
Experiment to make a self-catering PDF for Kindle with Python
Make a tky2jgd plugin with no practicality in QGIS Part 1