どうもmkです。
今回はmastdonのトゥートをDiscordに転送するBotを作る話です。
事の発端
node.jsとかphpとかが話に上がりましたが、Pythonistaの自分としてはPythonで書きたい(それしか書けない)
今回はPythonで書きますが、きっと誰かが他の言語で実装してくれるはず
GAS版 by Tマート店長
偉大なる先駆者であり、今回の発案者
Botの準備
https://discord.com/developers/applications
まずはここにアクセス
ここを押して
作るBotの名前を選択
今回は Nerv to Discord にしました
こんな感じの画面に飛ばされるので
Botタブに移動して Add Bot を押す
確認画面が出るので Yes, do it! を選択
写真はお好みで(Save Changes を押すのを忘れずに (1敗))
アクセストークンをコピー
OAuth2タブに移動して
scopes と書かれたところの bot にチェック
下の方に生成されたURLにアクセス
追加したいサーバーを選択して 認証 を押す
必要なパッケージのインストール
pip3 install discord
html2text
websockets
今回使うライブラリの用途
discord
ソースコード: https://github.com/Rapptz/discord.py
ドキュメント: https://discordpy.readthedocs.io/ja/latest/index.html
DiscordのAPIとの通信
html2text
ソースコード: https://github.com/aaronsw/html2text
ドキュメント: なし
mastdonのAPIから受け取ったトゥートの内容をmarkdownに変換
websockets
ソースコード: https://github.com/aaugustin/websockets
ドキュメント: https://websockets.readthedocs.io/en/latest/
mastdonのAPIとの通信
mastdonのトゥートを取得
import asyncio
import json
import html2text
import websockets
async def get_toot():
async with websockets.connect("wss://mstdn.jp/api/v1/streaming/?stream=public") as websocket:
while True:
data = json.loads(await websocket.recv())
if data["event"] == "update":
payload = json.loads(data["payload"])
print(html2text.html2text(payload["content"]))
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(get_toot())
今回のテスト用のコードです
Nervだとトゥートがあんまり流れて来ないので今回はmstdn.jpを使用していますあんまりよくないコードだね
解説
正直asyncioよく分かんない
async with websockets.connect("wss://mstdn.jp/api/v1/streaming/?stream=public") as websocket:
websocketに接続
data = json.loads(await websocket.recv())
Pythonのdict型として読み込み
if data["event"] == "update":
update イベントか確認 (delete イベントもある)
payload = json.loads(data["payload"])
最大の謎
何故か data[“payload”] がstr型と認識されるのでdict型に変換
print(html2text.html2text(payload["content"]))
markdownにして表示
Discordに送信
import asyncio
import json
import discord
import html2text
import websockets
TOKEN = "準備編で取得したアクセストークン"
CHANNEL_NAME = "Botが書き込むチャンネル名"
client = discord.Client()
@client.event
async def on_ready():
channel = discord.utils.get(client.get_all_channels(), name=CHANNEL_NAME)
async with websockets.connect("wss://unnerv.jp/api/v1/streaming/?stream=public") as websocket:
while True:
data = json.loads(await websocket.recv())
if data["event"] == "update":
payload = json.loads(data["payload"])
text = html2text.html2text(payload["content"]).splitlines()
images = payload["media_attachments"] if "media_attachments" in payload else None
embed = discord.Embed(title = text[0], description = "\n".join(text[1:]))
if images:
embed.set_image(url=images[0]["preview_url"])
await channel.send(embed = embed)
if len(images) > 1:
for image in images[1:]:
await channel.send(embed = discord.Embed(image = image["preview_url"]))
else:
await channel.send(embed = embed)
if __name__ == "__main__":
client.run(TOKEN)
本番用コードですクソコードとか言わないで
解説
@client.event
async def on_ready():
Botが起動時に実行される
channel = discord.utils.get(client.get_all_channels(), name=CHANNEL_NAME)
チャンネルを検索
text = html2text.html2text(payload["content"]).splitlines()
タイトルと内容を分割
images = payload["media_attachments"] if "media_attachments" in payload else None
トゥートが画像付きなら画像のURLのリストを、そうでなければ None を代入
embed = discord.Embed(title = text[0], description = "\n".join(text[1:]))
Embedを作成
embed.set_image(url=images[0]["preview_url"])
Embedに画像を追加
await channel.send(embed = discord.Embed(image = image["preview_url"]))
画像が複数あったら、画像のみのEmbedを送信
ライブラリを使えば約50行で作れます
やっぱりPythonが最強だね(宗教戦争勃発)
実際に動かしてみた
使ってみた感想
結構便利で使いやすい
タグとかで絞り込んだり、色分けとかしたらもっと使いやすくなりそう
最後に
豊富なライブラリ、そして簡単に書ける
君もそんなPythonを使ってみないか?遅いとか言わない
インデントがクソだって?
これを使いなさい
次回はFast APIをやるかもしれない
The post MastdonのトゥートをDiscordに転送する話 first appeared on FascodeNetwork Blog.