[PYTHON] Explanation and implementation of the XMPP protocol used in Slack, HipChat, and IRC

The common feature of ICQ, IRC, Yahoo Messenger, HipChat, Slack, and Google Talk, which are familiar to Ao, is that they all use the XML-based XMPP protocol for communication. Instant messaging services change rapidly, so I think it's important to keep the core part unchanged.

Speaking the XMPP protocol creates a bot that can use most message services. In this article, we will implement a bot that supports multiple message services using the library of Jabber, which developed and released XMPP.

What to make

A bot that logs in to both HipChat and Slack when it starts up, and deploys and executes commands when it talks to it. We decided to develop a special case of using Slack overseas and HipChat in Japan. ~~ It's hard during the transition period ~~ I think it's good to introduce new things one after another.

■ Slack スクリーンショット 2015-11-30 18.02.01.png

スクリーンショット 2015-11-30 16.14.11.png

■ HipChat スクリーンショット 2015-11-30 16.16.15.png

An overview of the XMPP protocol in 2 minutes

■ The specifications are published at rfc3920. ■ XML-based communication protocol developed by Jabber ■ The following 5-piece set of information required for connection スクリーンショット 2015-11-30 17.37.57.png

Communication format is XML


Server: <stream>
<message>...</message>
Client: <stream>
<message>...</message>
Server: <message>...</message>
Client: <message>...</message>
Server: <message>...</message>
Client: <message>...</message>
Server: </stream>
Client: </stream>

■ To use with Slack, enable XMPP Gate Way in Admin and pay out with each account ■ In HipChat, it can be used with the default settings.

XMPP was created by Jabber

You can easily implement an XMPP-speaking bot using the Jabber library jabberbot.

install

install


pip install jabberbot
pip install xmpppy
pip install lazy-reload
pip install requests
pip install simplejson

Bot for Slack and HipChat

bot.py


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

import sys
import time
import traceback
import logging
from jabberbot import botcmd, JabberBot, xmpp
import multiprocessing as mp


class ChatBot(JabberBot):
    """
    XMPP/Connect to HipChat's message service using Jabber bot
    """

    _content_commands = {}
    _global_commands = []
    _command_aliases = {}
    _all_msg_handlers = []
    _last_message = ''
    _last_send_time = time.time()
    _restart = False

    def __init__(self, config):
        self._config = config
        channel = config['connection']['channel']
        username = u"%s@%s" % (config['connection']['username'],
                               config['connection'].get('host',
                                                        config['connection']['host']))
        self._username = username
        super(ChatBot, self).__init__(
            username=username,
            password=config['connection']['password'])

        self.PING_FREQUENCY = 50  # timeout sec
        self.join_room(channel, config['connection']['nickname'])
        self.log.setLevel(logging.INFO)

    def join_room(self, room, username=None, password=None):
        """
Join Chat Room
        """
        NS_MUC = 'http://jabber.org/protocol/muc'
        if username is None:
            username = self._username.split('@')[0]
        my_room_JID = u'/'.join((room, username))
        pres = xmpp.Presence(to=my_room_JID)
        if password is not None:
            pres.setTag(
                'x', namespace=NS_MUC).setTagData('password', password)
        else:
            pres.setTag('x', namespace=NS_MUC)

        #Do not read message history when joining
        pres.getTag('x').addChild('history', {'maxchars': '0',
                                              'maxstanzas': '0'})
        self.connect().send(pres)


    def callback_message(self, conn, mess):
        """
Executed when a message is received
        """
        _type = mess.getType()
        jid = mess.getFrom()
        props = mess.getProperties()
        text = mess.getBody()
        username = self.get_sender_username(mess)

        print "callback_message:{}".format(text)
        print _type
        # print jid
        print props
        print username
        super(ChatBot, self).callback_message(conn, mess)


        #Respond if the inquiry contains a specific character string
        import time
        import random
        time.sleep(1)
        if 'Wish' in text:
            ret = ["Ryo. I'm sorry",
                   "Please wait for a while",
                   "http://rr.img.naver.jp/mig?src=http%3A%2F%2Fimgcc.naver.jp%2Fkaze%2Fmission%2FUSER%2F20141001%2F66%2F6169106%2F58%2F186x211xe15ffb8d1f6f246446c89d7e.jpg%2F300%2F600&twidth=300&theight=600&qlt=80&res_format=jpg&op=r",
                   "http://e-village.main.jp/gazou/image_gazou/gazou_0053.jpeg "]
            self.send_simple_reply(mess, random.choice(ret))

        if 'bug' in text:
            ret = ["I'll fix it later with my understanding",
                   "Recognized",
                   "We will respond after the meeting after this.",
                   "The latest version has been fixed, so I will reflect it."]
            self.send_simple_reply(mess, random.choice(ret))

    def send_message(self, mess):
        """Send an XMPP message
        Overridden from jabberbot to update _last_send_time
        """
        self._last_send_time = time.time()
        self.connect().send(mess)

def bot_start(conf):
    print "++++++++"
    print conf
    print "++++++++"
    bot = ChatBot(conf)
    bot.serve_forever()


class ChatDaemon(object):
    config = None

    def run(self):
        try:
            # Start Slack Bot
            process_slack = mp.Process(target=bot_start, args=(self.config_slack,))
            process_slack.start()

            # Start HipChat Bot
            process_hipchat = mp.Process(target=bot_start, args=(self.config_hipchat,))
            process_hipchat.start()
        except Exception, e:
            print >> sys.stderr, "ERROR: %s" % (e,)
            print >> sys.stderr, traceback.format_exc()
            return 1
        else:
            return 0


def main():
    import logging
    logging.basicConfig()
    config_slack = {
        'connection': {
            'username': '{{name}}',
            'password': '{{password}}',
            'nickname': '{{name}}',
            'host': '{{TeamName}}.xmpp.slack.com',
            'channel': '{{RoomName}}@conference.{{TeamName}}.xmpp.slack.com',
        }
    }
    config_hipchat = {
        'connection': {
            'username': '{{name}}',
            'password': '{{password}}',
            'nickname': '{{nickname}}',
            'host': 'chat.hipchat.com',
            'channel': '{{RoomName}}@conf.hipchat.com',
        }
    }

    runner = ChatDaemon()
    runner.config_slack = config_slack
    runner.config_hipchat = config_hipchat
    runner.run()


if __name__ == '__main__':
    sys.exit(main())

Bot logs in when launched

スクリーンショット 2015-11-30 17.34.18.png

I talked to HipChat and Slack

スクリーンショット 2015-11-30 16.38.34.png

I received the message

スクリーンショット 2015-11-30 16.38.06.png

Payment of setting information

■ 1.Slack SLACK must be enabled for XMPP Gateway in admin https://{{TeamName}}.slack.com/account/gateways

■ 2.HipChat https://{{組織名}}.hipchat.com/rooms/show/{{room_id}}

reference

rfc3920 XMPP (Jabber Protocol) Technical Memo XMPP introductory page for general users (provisional)

Summary

If you are considering introducing a new instant messaging service, it may make engineers happy if you check whether it supports the XMPP protocol.

If you write ʻos.subprocess ('deploy cmd hogehoge')in thecallback_message function`, you can implement any command, but I think you will be happier if you use an off-the-shelf HuBot.

Leave the chat service to the bot and write the code ╭ (・ ㅂ ・) و

Recommended Posts

Explanation and implementation of the XMPP protocol used in Slack, HipChat, and IRC
Explanation of edit distance and implementation in Python
Explanation and implementation of SocialFoceModel
[Reinforcement learning] Explanation and implementation of Ape-X in Keras (failure)
Explanation of CSV and implementation example in each programming language
Explanation and implementation of PRML Chapter 4
Explanation and implementation of ESIM algorithm
Explanation and implementation of simple perceptron
Verify the compression rate and time of PIXZ used in practice
Explanation and implementation of Decomposable Attention algorithm
Predict the amount of electricity used in 2 days and publish it in CSV
[Introduction to Python] Thorough explanation of the character string type used in Python!
Why the Python implementation of ISUCON 5 used Bottle
Fix the argument of the function used in map
[Implementation explanation] How to use the Japanese version of BERT in Google Colaboratory (PyTorch)
Used from the introduction of Node.js in WSL environment
Python --Explanation and usage summary of the top 24 packages
About testing in the implementation of machine learning models
Overview of generalized linear models and implementation in Python
A reminder about the implementation of recommendations in Python
Commands often used in the development environment during Python implementation
[Tips] Problems and solutions in the development of python + kivy
Get information from the Japan Meteorological Agency and notify Slack of weather warnings in the 23 wards of Tokyo
Implementation of quicksort in Python
Count the number of Thai and Arabic characters well in Python
Real-time display of server-side processing progress in the browser (implementation of progress bar)
Scraping the schedule of Hinatazaka46 and reflecting it in Google Calendar
Probability of getting the highest and lowest turnip prices in Atsumori
Notify the contents of the task before and after executing the task in Fabric
[Python] I thoroughly explained the theory and implementation of logistic regression
[Python] I thoroughly explained the theory and implementation of decision trees
Mathematical explanation of binary search and ternary search and implementation method without bugs
I tried to summarize the frequently used implementation method of pytest-mock
Get the title and delivery date of Yahoo! News in Python