てんこ製作

Tenco Works

PC間でメッセージ送信

サーバーで長時間動くpythonプログラムが処理終了したのを、他のPCで通知を受け取って感知したいと思いました。pythonプログラムからSlackに通知が投げれればできそう。

実現したいこと

  • pythonプログラムからSlackにメッセージを投げつける
  • 作業用PCでメッセージを受け取る

スクリプト起動
あとは放置
スクリプト起動 あとは放置
終了したら通知
終了したら通知
作業PC
作業PC
プロセスが動くサーバー
プロセスが動くサーバー
見たいときだけ
通知を参照
見たいときだけ 通知を参照
Viewer does not support full SVG 1.1

Slack側の設定

クライアントからSlackにメッセージを投げつける前に、Slack側でメッセージを受け取る準備が必要です。

slackではslack appによって機能追加をしていきます。外部と通知をやり取りするのも、Webhookのアプリを追加することで実現しています。今回はslackの外部から通知を受け取る機能が必要なので、Incomming Webhookをインストールします。

  • Slackでアカウントを作ってログインする
  • アカウントの設定の中でIncomming Webhookというslack appを追加する

slackの公式サイトでも方法が案内されています。

slack.com

slack appの追加

まずslackクライアントで「Slackをブラウズする」→「App」をクリックします。

Appを検索する画面になったら「webhook」で検索します。すると「Incoming Webhooks」というのが見つかるので、「追加」ボタンをクリックします。

「追加」ボタンをクリックするとブラウザが立ち上がってslack app directoryの画面が表示されるので、ここで「Slackに追加」ボタンをクリックします。

次に、どのチャネルで通知を受け付けるかを設定する画面になります。いずれかのチャネルを選択して、「Incoming Webhookインテグレーションの追加」ボタンをクリックします。

最後に追加が完了画面となります。ここで「Webhook URL」が表示されるので、メモっておきます。後ほどプログラム側からこのURLに対してHTTPリクエストを送信することでメッセージを送ることになります。

更に下の方には、curlコマンドを使ってメッセージを送る例も表示されているので、これもメモっておきます。

これでslack側のセットアップは完了です。

メッセージを送ってみる

メッセージを送る方法

slackでWebhookの設定をしたときに表示された例のようにcurlコマンドで送信してもいいですが、今回pythonプログラムの終了を通知したいという要件があります。

できたらpythonプログラムの中からメッセージ送信を指示したい。subprocess関数でcurlコマンドを起動してもいいんでしょうけど、pythonにはhttpリクエストを送信する関数も標準装備してるので、今回は直接httpリクエストを送る方法でやってみます。

pythonで送信する場合

ちょろっと関数で作って見ようと思います。

Incoming Webhookの設定画面に表示されていたcurlコマンドの例は、

curl -X POST --data-urlencode "payload={\"channel\": \"#general\", \"username\": \"webhookbot\", \"text\": \"これは webhookbot という名のボットから #general に投稿されています。\", \"icon_emoji\": \":ghost:\"}" https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxxxxxx

というものでした。

import urllib
import json

def slackmsg(text="",url="",channel="#general",username="webhookbot",icon_emoji=":ghost:"):
    payload = {
        "channel":channel,
        "username":username,
        "text":text,
        "icon_emoji":icon_emoji
    }
    headerdata = {
        "Content-Type": "application/json"
    }
    try:
        req = urllib.request.Request(url,data=json.dumps(payload).encode(),method="POST",headers=headerdata)
        with urllib.request.urlopen(req) as response:
            status = response.getcode()
    except urllib.error.URLError as e:
        print(e.reason)

要は、

  • 固定のリクエストヘッダーを付けて
  • データ部にjson形式でメッセージをわたして
  • POSTリクエストで
  • 指定されたurlにリクエストを送信する。

これでメッセージを送信できます。

httpリクエストはurllibrequest.Request()という関数で行いますが、ハマったのはその引数。

docs.python.org

The supported object types include bytes, file-like objects, and iterables of bytes-like objects.

https://docs.python.org/ja/3.9/library/urllib.request.html#module-urllib.request

公式のマニュアルは肝心なところが未訳っぽくて英語になってますが、data引数の型がstr型ではだめで、バイト列に変換する必要があるみたい。

なので、data=json.dumps(payload)ではなくdata=json.dumps(payload).encode()とする必要があります。

slackmsg("これは webhookbot という名のボットから #general に投稿されています。","https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxxxxxx")

という感じで、メッセージ送信成功😃