IT業務効率化

PythonとSlackでtipsボットを作る。その3【受けとったtipsを保存する】

python-slack

はじめに

ipsボット作成の3記事目です。

Slackに自動で通知を送る機能を1つ目の記事で作りました。

tipsボットに向けてtipsを覚える命令をした時に、その命令を取得するところを前回の2つ目の記事で作りました。

今回はそのtipsを保存、削除、リスト表示するところまで作成しようと思います。

受け取ったtipsをどう管理するか

jsonで保持します

tipsが入っているのはjsonでもDBでもいいのですが、tips bot 自体はもっと気軽に使いたいので、環境が変わるたびにDBを用意するのも面倒です。tipsも1つの部署なら100は超えないと思うので、jsonで大丈夫でしょう。

カテゴリ分けします

今後tipsが増えていく時にカテゴリできるようにjsonを分けようと思います。分け方は暫定ですが、チャンネル毎に分けようかと思います。チャンネル名の取得方法はわかったのですが、先頭に_のついている関数を使う必要があり、個人的に好きではないのでやめます。チャンネル名ではなくチャンネルIDで進めることにします。チャンネルIDは以下のように取得できます。

@respond_to('learn tip(\d*) (.*)')
def learn_tip(message, tip_number, tip_msg):
    channel_id = message.body["channel"]
    message.reply('I learned tips{} as {}'.format(tip_number, tip_msg))

tips_botのファイル構造を整理する

.
├── functions
│ └── tips_manager.py
├── plugins.py
├── run.py
├── send_tips.py
├── slackbot_settings.py
├── slackbot_settings.py.sample
└── tips

このようなファイル構成で進めていこうと思います。slackbot_settings.pyの中に

PLUGINS = [
    'plugins'
]

とう記述がありますが、plugins.plugin1という形でつないでいくと、pluginsディレクトリ配下のplugin1というpythonファイルを参照しにいくため、より管理がしやすくなるかと思います。しかし今回の目標としては、階層を深くしてファイル分けしなくても良さそうだと判断したので、このファイル構成で進めようと思います。

tipsを管理するPythonコードを作成する

tipsのjsonがないときは、空のjsonファイルを作成する

これはchannelで初めてtipsボットを使いたい時に必須になる機能です。tipsは基本的にjsonファイルで管理する予定でいるのですが、チャンネル専用のjsonファイルが存在しない時には最初にからで作成します。

def _touch_tips_if_not_exist(tips_path):
    """
    If it is the first time that you to mamage tips,
    create a new json named by channel_id
    """
    is_exists = os.path.exists(tips_path.format(tips_path))
    if is_exists:
        return
    with open(tips_path, "w") as tips_file:
        tips_file.write("{}")

新しく学んだtipを保存する

tipの新規追加と更新は今回は同じ関数にしてしまいます。辞書型(連想配列)で取得して、tip numberとtipで対にします。辞書型に追加していく形をとるので、同じtip numberであれば自動的に上書きされます。

def update_tips(channel_id, tip_number, tip):
    """
    Parameters
    ----------
    channel_id : str
    tip_number : integer
    tip : str
    """
    tips_path = "tips/{}.json".format(channel_id)
    _touch_tips_if_not_exist(tips_path)

    with open(tips_path, "r") as tips_file:
        tips_data = json.load(tips_file)

    tips_data[tip_number] = tip

    with open(tips_path, "w") as tips_file:
        json.dump(tips_data, tips_file, ensure_ascii=False)

 

こんな形で書いてみました。そしてテスト代わりに下記の実行してみます。予定では3行目の実行でtip number 1の帰ろうは、帰りたいに上書きされるはずです。

update_tips("channel_id", 1, "帰ろう")
update_tips("channel_id", 12, "幹事は持ち回り制")
update_tips("channel_id", 1, "帰りたい")

この3つのコードを実行してみました。jsonファイルを確認してみます。

{"1": "帰ろう", "12": "幹事は持ち回り制", "1": "帰りたい"}

あっれ〜?

printを挟むとよくわかりました。jsonファイルにする時にkeyのデータ型が整数から文字列になっていました。そして新しくtipが追加されるときはkeyが整数型で登録されるので、tip numberが重複していると見なされないのです。

勉強していくと大切なことを知りました。

jsonのキーは文字列型である

これを今回初めて知りました。そうやって考えると、キーに数値を持ってきていること自体ナンセンスなのでしょうか?でもリストにするとデータを削除していく時に、リストの番号がズレていくので、嫌なんですよね。。

うん。。。Pythonの中でも文字列で扱いましょう。

update_tips("channel_id", "1", "帰ろう")
update_tips("channel_id", "12", "幹事は持ち回り制")
update_tips("channel_id", "1", "帰りたい")

上を実行した結果保存されたjsonがこちらです。

{"1": "帰りたい", "12": "幹事は持ち回り制"}

成功しました。帰ろうが帰りたいで上書きされています。

ではjsonを更新するための関数が完成したので、slackボットに実装したいと思います。

from slackbot.bot import respond_to
from slackbot.bot import listen_to
from functions.tips_manager import update_tips
import re


@respond_to('learn tip(\d*) (.*)')
def learn_tip(message, tip_number, tip_msg):
    channel_id = message.body["channel"]
    update_tips(channel_id, tip_number, tip_msg)
    message.reply('I learned tips{} as {}'.format(tip_number, tip_msg))

update_tipsをインポートして、learn_tipsの中に実装します。このファイルを実行して、tipsボットに話しかけてみます。

覚えたと言ってくれていますね。message.replyまで処理が通っているので、update_tipsは実行されているはずです。jsonファイルを確認しに行きます。

できました!チャンネルIDをファイル名に保つため、チャンネル毎にtipsを覚えています!

おわりに

今回はSlack上で「@tips_bot tip番号 tip内容」と喋るだけでtipsをjsonファイルに保存するところまで完成しました。

次回こそは、tipsリストの表示、tipsの削除まで実装したいと思います。

読んでいただきありがとうございました。

git hubのソースコード

 

前回の記事次回の記事

ABOUT ME
hirayuki
今年で社会人3年目になります。 日々体当たりで仕事を覚えています。 テーマはIT・教育です。 少しでも技術に親しんでもらえるよう、noteで4コマ漫画も書いています。 https://note.mu/hirayuki