🦜
Nextcloud Talkのチャットボットを仕様通りに構築する
TL;DR
- Nextcloudサーバー上でコマンド
occ talk:bot:install
で作成する - Webhook→トークへのレスポンス
- レスポンス先
NextCloudのURL/ocs/v2.php/apps/spreed/api/v1/bot/{トークID}/message
- ヘッダ
-
X-Nextcloud-Talk-Bot-Random
:任意の64桁16進文字列 -
X-Nextcloud-Talk-Bot-Signature
:X-Nextcloud-Talk-Bot-Randomと返信メッセージのバイト列をBot作成時のシークレットを用いてHMACで暗号化した16進文字列 -
OCS-APIRequest
:true
-
- ボディ
- JSON:
{"message": "返信メッセージ"}
- JSON:
- レスポンス先
背景
巷に溢れているNextcloud Talkのボットの構築手順記事は、厳密には特定ユーザーのAPIを呼び出してそのユーザーとして投稿するという疑似ボットばかりであり仕様として提供されている本来のボットではないものがほとんどだったので、仕様通りのボットを構築する。
Bot & Webhooks仕様
手順
ブラウザからは設定することができず、直接Nextcloudサーバーにログインしてコマンド実行する必要がある。
またコマンド実行ユーザーはNextcloudの実行ユーザーである必要がある。
Docker版であれば-u 33でコンテナに入る。
前提
- Botを有効にしたいトークのモデレータであること
- Docker版での手順
1. Bot作成
command
docker exec -it -u 33 コンテナ名 \
./occ \
talk:bot:install \
ボット名 \
シークレット(40文字以上128文字以下) \
WebhookエンドポイントURL \
"ボットの説明文字列"
作成確認
command
docker exec -it -u 33 nextcloud \
./occ \
talk:bot:list
+----+---------+------------------+-------------+-------+-------------------+
| id | name | description | error_count | state | features |
+----+---------+------------------+-------------+-------+-------------------+
| 1 | TestBot | Test Description | 0 | 1 | webhook, response |
+----+---------+------------------+-------------+-------+-------------------+
2. Bot有効化
トークの会話設定>ボット>作成したBotをEnableにする。
3. Webhook実装
Flaskで引用付きでオウム返しするBotの例
server.py
import hmac
import json
import secrets
from hashlib import sha256
import requests
from flask import Flask, request
def main() -> None:
app: Flask = Flask(__name__)
@app.post("/")
def nextcloud():
message: str = json.loads(request.json["object"]["content"])["message"]
token: str = secrets.token_hex(32)
print(
requests.post(
f"{request.headers['X_NEXTCLOUD_TALK_BACKEND']}ocs/v2.php/apps/spreed/api/v1/bot/{request.json['target']['id']}/message",
headers={
"X-Nextcloud-Talk-Bot-Random": token,
"X-Nextcloud-Talk-Bot-Signature": hmac.new(
"シークレット".encode(),
f"{token}{message}".encode(),
"sha256",
).hexdigest(),
"OCS-APIRequest": "true",
},
json=dict(
message=message,
replyTo=int(request.json["object"]["id"]),
referenceId=sha256(secrets.token_hex(32).encode()).hexdigest(),
silent=False,
),
).text
)
return ""
app.run(host="0.0.0.0", port=Webhookのポート番号, threaded=True)
if __name__ == "__main__":
main()
4. 動作例
- トークメッセージ
- Flaskでの返信POST結果
<?xml version="1.0"?> <ocs> <meta> <status>ok</status> <statuscode>201</statuscode> <message>OK</message> </meta> <data/> </ocs>
ユーザーのAPI呼び出しとは異なりファイル添付等できず、シンプルなテキストメッセージかリアクションしか投稿できない(トークメッセージのMarkdownには対応)。
またボットのアイコンも簡素な画像なうえ変えられなさそうで、Botとしては最低限のかなり貧弱な機能しか提供されていない。
Discussion