Python: пишем Slackbot
Лично мне он пригодится для автоматизации различных процессов. Каких не скажу. Для работы бота нам понадобится веб-сервер т.е. бот должен быть доступен с внешки. Когда в slack будем давать команды, то slack api будет дергать нашего бота, который в ответ должен будет возвращать что-либо.
Создаем приложение
Идем на стрницу api.slack.com/apps и регистрируем приложение.
Задаем название и выбираем workspace для которого оно создается.
В Features->Oauth & Permissions в Add features and functionality выбираем Bots.
В OAuth & Permissions->Scopes->Bot token scopes добавляем: app_mentions:read, channels:history, channels:read, channels:write, chat:write, commands, im:history, im:read, im:write. Далее нажимаем "Install App to Workspace".
После установки приложения либо сразу высветится Bot User OAuth Access Token либо найти его можно в OAuth & Permissions.
Если пойдем в Basic Information, то найдем Signing Secret и Verification Secret (в скрине последний отсутвует). Client ID не понадобится.
Конфигурация
Теперь создадим config.ini приложения и соответственно конфиг надо заполнить своими данными:
[SLACK]
SIGNING_SECRET = тут signing secret
VERIFICATION_TOKEN = тут verification secret
OAUTH_TOKEN = тут bot user oauth access token
DOMAIN = тут домен по которому будет доступен бот, например slackbot.example.com
LISTEN = 127.0.0.1
PORT = 3000
[LOG]
LOG_FILE = main.log
LOG_SIZE = 10000000
LOG_ROTATE = 5
Подразумевается что у нас уже есть адрес slackbot.example.com потому пойдем и настроим nginx:
upstream bot {
server 127.0.0.1:3000;
}
...
location / {
proxy_pass http://bot;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
Теперь накидаем стартовый скрипт в /etc/systemd/system/slackbot.service:
[Unit]
Description=Slackbot
After=network.target
[Service]
Type=simple
PIDFile=/var/run/slackbot.pid
WorkingDirectory=/opt/slackbot
ExecStart=/opt/slackbot/main.py
Restart=always
User=nobody
Group=nobody
[Install]
WantedBy=multi-user.target
Тело бота
Установка пакетов:
pip3 install flask flask_ini slackeventsapi slackclient
Ну и собственно накидаем main.py т.е. каркас приложения, который на приветствие будет отвечать нам:
#!/usr/bin/python3
from flask import Flask, Response, jsonify, request
from flask_ini import FlaskIni
from slackeventsapi import SlackEventAdapter
import os
from threading import Thread
from slack import WebClient
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
with app.app_context():
app.iniconfig = FlaskIni()
app.iniconfig.read('./config.ini')
greetings = ["hi", "hello", "hello there", "hey"]
SLACK_SIGNING_SECRET = app.iniconfig.get('SLACK', 'SIGNING_SECRET')
SLACK_TOKEN = app.iniconfig.get('SLACK', 'OAUTH_TOKEN')
VERIFICATION_TOKEN = app.iniconfig.get('SLACK', 'VERIFICATION_TOKEN')
DOMAIN = app.iniconfig.get('SLACK', 'DOMAIN')
HOST = app.iniconfig.get('SLACK', 'LISTEN')
PORT = app.iniconfig.getint('SLACK', 'PORT')
LOG_FILE = app.iniconfig.get('LOG', 'LOG_FILE')
LOG_SIZE = app.iniconfig.getint('LOG', 'LOG_SIZE')
LOG_ROTATE = app.iniconfig.getint('LOG', 'LOG_ROTATE')
slack_client = WebClient(SLACK_TOKEN)
logging.basicConfig(
handlers=[RotatingFileHandler(LOG_FILE, maxBytes=LOG_SIZE, backupCount=LOG_ROTATE)],
level=logging.INFO,
format="[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s",
datefmt='%Y-%m-%dT%H:%M:%S'
)
app.logger.info("============= Slackbot start! =============")
@app.errorhandler(Exception)
def server_error(err):
app.logger.error(f"App error: {err}")
return "Forbidden", 403
@app.errorhandler(ZeroDivisionError)
def server_error(err):
app.logger.error(f"Server error: {err}")
return "Cannot divide by 0", 500
@app.route("/")
def event_hook(request):
json_dict = json.loads(request.body.decode("utf-8"))
if json_dict["token"] != VERIFICATION_TOKEN:
app.logger.error(f"Client error: {json_dict}\nReturn status 403")
return {"status": 403}
if "type" in json_dict:
if json_dict["type"] == "url_verification":
response_dict = {"challenge": json_dict["challenge"]}
return response_dict
app.logger.error(f"Server error: {json_dict}\nReturn status 500")
return {"status": 500}
return
slack_events_adapter = SlackEventAdapter(
SLACK_SIGNING_SECRET, "/slack/events", app
)
@slack_events_adapter.on("app_mention")
def handle_message(event_data):
def send_reply(value):
event_data = value
message = event_data["event"]
if message.get("subtype") is None:
command = message.get("text")
channel_id = message["channel"]
# event commands (mentions)
if any(item in command.lower() for item in greetings):
message = (
"<@%s> ready to serve!"
% message["user"]
)
slack_client.chat_postMessage(channel=channel_id, text=message)
thread = Thread(target=send_reply, kwargs={"value": event_data})
thread.start()
return Response(status=200)
if __name__ == "__main__":
app.run(debug=True, host=HOST, port=PORT)
Запускаем бота:
systemctl daemon-reload
systemctl enable slackbot
systemctl start slackbot
В Event subscriptions включаем Enable events и в Request URL указываем: https://slackbot.example.com/slack/events, чтобы включить реакции на события. Если все хорошо, то статус Request URL поменяется на Verified иначе получается, что бот еще не доступен для внешки и slack api не может достучаться до бота. В Subscribe bot events добавляем: app_mention, message.im, message.channels.
Теперь в slackchat создаем тестовый канал...ну пусть будет #bot_test и приглашаем туда бота:
/invite slackbot
Где slackbot это название созданного ранее приложения в api.slack.com.
Теперь если поприветствуем его командой @slackbot hi, то он ответ "ready to serve".
Собственно после блока кода:
if any(item in command.lower() for item in greetings):
message = (
"<@%s> ready to serve!"
% message["user"]
)
slack_client.chat_postMessage(channel=channel_id, text=message)
можно добавлять свой код. Например можем добавить такой код:
if "blabla" in command:
message = "Хватит блакать!"
slack_client.chat_postMessage(channel=channel_id, text=message)
Теперь если написать @slackbot blabla, то он ответит "Хватит блакать!".
Также боты в slack поддерживают команды с слешем и ответ на такие команды будет виден только вам. Для этого в Slash commands регистрируем команду скажем test и Request URL: https://slackbot.example.com/test. После slack_events_adapter добавляем следующий код:
# slash commands
@app.route('/test', methods=['POST'])
def test():
if request.form['token'] == VERIFICATION_TOKEN and request.form['text']:
command = request.form['text']
payload = {'text': 'Это тестовое сообщение'}
return jsonify(payload)
Теперь если в slackchat набрать команду /test, то бот ответит "Это тестовое сообщение".
В плане Slack commands еще есть такая ремарка, что если уже кто-то в твоем workspace создал бота и уже зарегистрировал команду /test, то команда /test твоего бота не будет работать. Потому такие вещи надо будет проверять (тут только методом тыка).
Оригинальный источник: https://medium.com/developer-student-clubs-tiet/how-to-build-your-first-slack-bot-in-2020-with-python-flask-using-the-slack-events-api-4b20ae7b4f86