もっと詳しく

どうも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.