Python
Python3
bot
Slack
slackbot

感謝の気持ち(イイね!!)を可視化するbotをpythonバージョンで作って見た

はじめに

感謝の気持ちを可視化するbotを作りました。

機能としては単純で、以下の機能を持ちます
・「iine」& @user_nameでメンションをつけると1イイね付加
・「yokunaine」& @user_nameでメンションをつけると1イイね減少

スクリーンショット 2018-03-31 12.14.05.png

先日Innovator Japan Engineers’さんのブログでこの機能を拝見し、自分でも作成したい!と思い作成しました

「感謝の気持ち」をストックして可視化するSlackBotを作ってみた

Innovator Japan Engineers’さんはgoogle spread sheetで実装されてましたが、私は所属する会社の関係上、python-botで実装しました

実装環境

  • botフレームワーク:python-bot
  • 利用言語:python3
  • vagrant-file:以下の通り(synced_folderオプションで開発しやすいようホストと同期させてます)
$ cat vagrant/Vagrantfile
 Vagrant.configure(2) do |config|
  config.vm.box = "bento/centos-6.7"

  config.vm.define "dev" do |node|
    node.vm.network "private_network", ip: "192.169.20.20"
    node.vm.synced_folder "ホストPCのパス", "/root/project",
    id: "vagrant-root", :nfs => false,
      :owner => "vagrant",
      :group => "vagrant",
      :mount_options => ["dmode=777,fmode=777"]
  end
end

python botのフレームワークであるpython-botは以下の記事がとても参考になりますので参照ください。

PythonでSlackbotを作る(1) – ビットログ
PythonでSlackbotを作る(2) – ビットログ
PythonのslackbotライブラリでSlackボットを作る

ファイル構成とディレクトリ

上記のURLの内容にしたがってslack-botを構築すると以下のような構成になると思います

slackbot         # プログラムをまとめるディレクトリ。名前はなんでも良い
├─ run.py        # このプログラムを実行することで、ボットを起動する
├─ slackbot_settings.py   # botに関する設定を書くファイル
└─ plugins                # botの機能はこのディレクトリに追加する
   ├─ __init__.py         # モジュールを示すためのファイル。空で良い
   └─ my_mention.py       # botの各機能を追記していくファイル。今回このファイルを編集する

星マークがついているディレクトリとファイルを新規に作成します。

slackbot
├─ run.py
├─ slackbot_settings.py
└─ plugins
   ├─ __init__.py
   └─ my_mention.py
★   └─ scripts # script置き場のディレクトリ
★       ├─ reply_iine_number.py # イイね計算機能が記載されている
★       ├─ iine.csv # curlコマンドが記載されるスクリプト
★       ├─__init__.py

コードの流れ

簡単ですがコードの流れは以下となります
slackで「iine @hogehoge」でコメント
👇
iineに反応してmy.mention.pyのデコレータが呼び出される。add_iine関数の実行
👇
my.mention.pyのadd_iine関数からreply_iine_number.pyが呼び出される
👇
iineされたことがあるかないかをチェックし、iine.csvファイルを編集
👇
iineの数を計算
👇
結果をslackへポスト

my_mention.py

# coding: utf-8

from slackbot.bot import respond_to     # @botname: で反応するデコーダ
from slackbot.bot import listen_to      # チャネル内発言で反応するデコーダ
from slackbot.bot import default_reply  # 該当する応答がない場合に反応するデコーダ
import sys
import os
import requests
######作成したスクリプトの読み込み########
from slackbot_settings import *
from plugins.scripts.reply_iine_number import ReturnAmountOfIine

# 関数内で使う変数を定義
script_dir = os.path.abspath(os.path.dirname(__file__))

# @respond_to('string')     bot宛のメッセージ
#                           stringは正規表現が可能 「r'string'」
# @listen_to('string')      チャンネル内のbot宛以外の投稿
#                           @botname: では反応しないことに注意
#                           他の人へのメンションでは反応する
#                           正規表現可能
# @default_reply()          DEFAULT_REPLY と同じ働き
#                           正規表現を指定すると、他のデコーダにヒットせず、
#                           正規表現にマッチするときに反応
# message.reply('string')   @発言者名: string でメッセージを送信
# message.send('string')    string を送信
# message.react('icon_emoji')  発言者のメッセージにリアクション(スタンプ)する
#                               文字列中に':'はいらない

@respond_to('メンション')
def mention_func(message):
    message.reply('私にメンションと言ってどうするのだ') # メンション

# いいねの数を増やして、合計イイね数を返信する
@listen_to('iine')
def add_iine(message):
    iine = ReturnAmountOfIine(message, script_dir)
# イイねされたことがある人かどうかを調べる
    check_result = iine.check_csv_file()
    if check_result == "exist":
        iine.add_iine_to_csv_file()
    else:
        iine.add_people_to_csv_file()
# イイねの数を調べる
    number_of_iine = iine.count_iine()

    post_message = "に1いいねプレゼントぽめ。合計いいね数は『{}』ぽめ".format(number_of_iine)
    requests.post(URL, data = iine.return_post_message(post_message))

# いいねの数を減らして、合計イイね数を返信する
@listen_to('yokunaine')
def remove_iine(message):
    iine = ReturnAmountOfIine(message, script_dir)
# イイねされたことがある人かどうかを調べる
    check_result = iine.check_csv_file()
    if check_result == "exist":
        iine.remove_iine_to_csv_file()
# イイねの数を調べる
        number_of_iine = iine.count_iine()
# message.sendではuser idなどがslack上でそのままでてしまうため、postを使う
        post_message = "のいいねをとりあげちゃうぽめ。合計いいね数は『{}』ぽめ".format(number_of_iine)
        requests.post(URL, data = iine.return_post_message(post_message))
    else:
        message.send('その人は一回もいいねされてないぽめ')

reply_iine_number.py

my_mention.pyから呼び出されるモジュールです。
「iine」が実行された場合は

  1. イイねされたことがある人かチェック
  2. イイねされたことがある人は1を追加
  3. ない人はcsvファイルの末尾にslackのユーザID,1(イイね数)の形式で記載

という順番で関数が実行されていきます
pythonでcsvファイルを編集する方法がよくわからず、編集する際はfileinputモジュールを使って標準出力をそのままファイルに書き込むような実装にしております(気持ち悪いのでなんとかしたい・・・)

# coding:utf-8
import os
import re
import sys
import requests
from slackbot_settings import *
import csv
import fileinput


class ReturnAmountOfIine():
    def __init__(self, message, script_dir):
        self.message = message.body['text']
# userid(slack場では@takahiro_shimamuraのような形で取り出せる)
        # self.userid = message.body['user']
        # iineをしたuseridを以下の正規表現でとりだす
        self.userid = re.search(r'(?<=<@).+?(?=>)', message.body['text']).group(0)
# 投稿元のchannel id
        self.channel_id = message.body['channel']

        self.csv_path = os.path.join(script_dir, "scripts", "iine.csv")

# 対象のuserがすでにcsvファイルにいるかを判定する
    def check_csv_file(self):
        with open(self.csv_path, 'r') as f:
            reader = csv.DictReader(f)
            for row in reader:
                if row[u'people'] == self.userid:
                    return("exist")
            return("not_exist")

# csv fileを編集し、1増やす
# fileinputモジュールを用いることにより、標準出力をcsv fileに書き込む
    def add_iine_to_csv_file(self):
        with fileinput.input(self.csv_path, inplace=True) as f:
# for文でcsvファイルを出力し、イイねされたユーザの名前に該当されたら数を一つ増やす
            for line in f:
# 行末の改行を削除する
                line = line.rstrip('\r\n')
                if line.startswith(self.userid):
                    new_iine_val = (int(line.split(",")[1]) + 1)
                    print(self.userid+","+str(new_iine_val))
                    continue
                print(line)

# csv fileを編集し、1減らす
    def remove_iine_to_csv_file(self):
        with fileinput.input(self.csv_path, inplace=True) as f:
# for文でcsvファイルを出力し、イイねされたユーザの名前に該当されたら数を一つ増やす
            for line in f:
                line = line.rstrip('\r\n')
                if line.startswith(self.userid):
                    new_iine_val = (int(line.split(",")[1]) - 1)
                    print(self.userid+","+str(new_iine_val))
                    continue
                print(line)

    def add_people_to_csv_file(self):
        data = [self.userid,int(1)]
        with open(self.csv_path, 'a') as f:
# 書き込み用writeオブジェクトを作成
# 書き込みの際末尾を改行する
            writer = csv.writer(f, lineterminator='\n')
            writer.writerow(data)

    def count_iine(self):
        with open (self.csv_path, 'r') as f:
            for line in f:
# 行末の改行を削除する
                line = line.rstrip('\r\n')
                if line.startswith(self.userid):
                    return line.split(",")[1]


    def return_post_message(self, post_message):
        post_json = {
            'token': API_TOKEN,
            'text': "<@"+self.userid+">"+post_message,
            'channel': self.channel_id,
            'username': "pomeranian_bot",
            'link_names': 1,
            "icon_emoji": ":pomeranian_bot:"
        }       
        return post_json

iine.csv

iine.csvはiineされた人が記載されていくファイルです
以下のようにslack上iineされた人とそのイイね数が計算されていきます

people,number_of_iine
A2LV25R8C,7
M6BMPT6GG,1

最初にファイルを作成する際はindexであるpeople,number_of_iineと改行のみのファイルにしてください

people,number_of_iine


各ファイルを配置後、python run.pyでbotを起動させ、
slackで「iine @hogehoge」と実行すると冒頭のようにbotが「イイね」された数を計算し、返してくれます

本botのコードは以下の通り、git hubにおきましたので何かお役に立てると幸いです。(汚くてすいません)
https://github.com/takahirono7/bot-iine