TL;DR
x.com
で始まるリンクを読み取るとvxtwitter.com
に変換してくれるDiscord Botを作ったよ
ホスティングはshuttleでやったよ
作成経緯
Twitterが色々変更されていく中で、Discordでリンクを自動で展開する機能がなくなった(埋め込み)
ので、埋め込みを表示してくれるリダイレクトのリンクができた(fxtwitter
fixup
とか)
でもわざわざ打ち直すの面倒
→Botにしようぜ
機能
イベントハンドラ
Messageにリンクが含まれていたら、
- Bot でない
- 正規表現にマッチする文字列が存在するか
を判定する。
それらが通った場合、マッチした条件にあう文字列を使ってhttp://vxtwitter.com
のリンクに加工して、そのメッセージにリプを返す
#[async_trait]
impl EventHandler for Bot {
async fn message(&self, _ctx: Context, msg: Message) {
if msg.author.bot {
return;
}
let Some((username, hash)) = match_url(&msg.content) else {
return;
};
let reply = format!("https://vxtwitter.com/{}/status/{}\n", username, hash);
check_msg(msg.reply(&_ctx.http, reply).await);
}
async fn ready(&self, _: Context, ready: Ready) {
info!("{} is connected!", ready.user.name);
}
}
正規表現で必要なパラメータを抽出する
正規表現をすると厳密に必要なパラメータを取り出せる(ただしセンスがいる)
// 正規表現マッチングを行う関数
fn match_url(content: &str) -> Option<(String, String)> {
let regex = Regex::new(
r"https:\/\/(x|twitter)\.com\/(?<username>[a-zA-Z0-9_]{1,16})\/status\/(?<hash>[0-9]+)",
)
.unwrap();
regex
.captures(content)
.map(|caps| (caps["username"].to_string(), caps["hash"].to_string()))
}
部分的なマッチパターンの前に?<hoge>
とやるとあとで同じ文字列をキーにしてマッチした値を取得できる
今回だと?<username>
?<hash>
が該当する
Shuttleのセットアップ
Shuttleでアカウント作って(GitHubで連携すると楽) NewProject
で出てくるコマンドを打つ
cargo install cargo-shuttle
cargo shuttle init # 対話型のやりとりで設定できる(使用するフレームワークを聞かれたときに今回はSerenityを選択する)
cargo shuttle run # ローカル実行
cargo shuttle deploy # shuttle.rsのPJにデプロイできる
新規PJで始めることをおすすめします。既存のレポのディレクトリを指定するとうまくインストールされなかったりデプロイに失敗します1
機密変数について
今回扱うDiscord BotではDISCORD_TOKEN
が該当する
これを生成されるSecrets.toml
にDISCORD_TOKEN="Your secret discrod token"
のように記入する
また、.gitignore
にSecrets.toml
を追加すること(ドキュメントにもこう書いてあるんだけど、なぜ最初から追加されていないかは謎)
Discord Botの作成について
Applicationsでアプリ作って権限設定してBotの欄でTokenをコピーしてSecrets.toml
に貼る。
詳しくは下記参考にしてください。
Botの作成方法はほとんど変わりませんが、3年以上前の記事のためすべてを参考にせず SerenityのドキュメントやShuttleでのチュートリアルを必ず参照してください。
Shuttleのアイドリングについて
デプロイとかもろもろしているとこんなログが出ます
PS C:\Users\raiga\dev> cargo shuttle init
What do you want to name your project?
It will be hosted at ${project_name}.shuttleapp.rs, so choose something unique!
✔ Project name · <hoge>
Where should we create this project?
✔ Directory · <huga>
Shuttle works with a range of web frameworks. Which one do you want to use?
✔ Framework · serenity
Creating project "vxbot" in "C:\Users\raiga\dev\<huga>"
Hint: Check the examples repo for a full list of templates:
https://github.com/shuttle-hq/shuttle-examples
Hint: You can also use `cargo shuttle init --from` to clone templates.
See https://docs.shuttle.rs/getting-started/shuttle-commands
or run `cargo shuttle init --help`
✔ Claim the project name "<hoge>" by starting a project container on Shuttle? · yes
Project "<hoge>" is ready
Your project will sleep if it is idle for 30 minutes. ### <- ここ!!!!!!!!!!
To change the idle time refer to the docs: https://docs.shuttle.rs/getting-started/idle-projects
Your project will sleep if it is idle for 30 minutes.
ドキュメントには、「2 calls/minないとスリープするよ。スリープ状態の復帰はCLIからでしかできないよ」みたいなことが書いてある。Botに関してはこの仕様はつらいので、設定し直す必要がある。
cargo shuttle project restart --idle-minutes 0
とするとスリープしないようにできる。これはドキュメントでも言及されているので問題無し。
念のため、status
コマンドでアイドル時間が0なことを確認する。
PS C:\Users\raiga\dev\vxbot_shuttle> cargo shuttle project status
Project "vxbot" is ready
Idle minutes: 0 # きちんと設定されている
動作例
Repo
最後に
Discordで高専生を中心としたメンバーが楽しくやっています。
-
そもそも既存のSerenityBotだと
#[shuttle_runtime::main]
とか#[shuttle_secrets::Secrets] secret_store: SecretStore,
みたいなShuttle特有の書き方を反映するのが少し大変。今回は小規模なPJだから1時間ほど溶かすにとどまりましたが、大規模なアプリの場合(データベースとかあったりすると)移行がかなり大変かも(そういった移行がし易いように設計しろという話は尤もすぎる) ↩