
Xの特定アカウントの投稿を、Misskeyにミラー投稿する個人用botを作ってきました。botの核となる部分は@the-convocation/twitter-scraperという、Cookieに保存されたXのトークンを使って認証、そしてXの投稿を取得することができるというOSSです。
設計は基本Claudeと壁打ちして、コード部分については9割がClaude製、人間は手直しを少々という感じでした。Xの認証周りでだけ少し詰まったので、そこら辺について重点的に書ければいいかなと思います。逆に言うとそれ以外詰まることはありませんでした。AIって本当に凄いですね。
以上4つです。フォロバ機能については別につけなくても良かったかもなとか思ったりしています。そのうち環境変数から設定できるようにしましょうかね。
やっていることがシンプルな分、中身も非常にシンプルです。
主にこの6ステップとなります。起動後は3~6をループする感じです。これに、以前作った時報botについてたフォロバ機能なんかをそのまま流用したものが完成品です。
Misskeyのクライアントのあれこれについてはコードを見てくださればと思います。とてもシンプルでわかりやすいので助かってます。しゅいろママありがとう。
で、問題のXの認証についてです。当初は現在のtwitter-scraperではなくそれを元にしたagent-twitter-clientという別のライブラリを使ってXの投稿取得を試みていました。初めのうちはユーザーID, パスワード, メールアドレスなんかを使った簡単な認証方式でできないものかと格闘していたのですが、何をどう頑張っても401 Unauthorizedが出て認証に失敗してしまっていました。
その後、認証方式をcookieから取得できるct0とauth_tokenを使うものに変更したのですが、こちらも変わらず401を吐かれてしまいました。色々試して他の方法がなくなってしまったので、最終的に使うことになったtwitter-scraperにライブラリを切り替えました。
切り替え以降も、イーロンくんがtwitterからXに変えたりした影響で、トークンを渡すドメインが違っていたりなんだりでつまりはしましたが、最終的に以下のようなコードでツイートを取得できるようになりました。取得対象のアカウントをusername、取得数をcountとしてぶん投げる感じですね。当たり前ですが、認証とかはこれ走らせる前に通しています。
async getRecentTweets(username: string, count: number = 20): Promise<Tweet[]> {
const tweets: Tweet[] = [];
for await (const tweet of this.scraper.getTweets(username, count)) {
tweets.push(tweet);
}
return tweets;
}
見出し通り、画像の投稿については少しだけ工夫というか、misskeyの仕様を活かした設計になっています。具体的には、
misskey-jsのnote/createに渡して投稿という流れです。X側の画像のURLをmisskeyの投稿に直接貼るのはできないので、一度ドライブ経由でアップロードする感じになっています。多少なりともドライブの容量を圧迫してしまうので、長期的な運用を予定している場合は、misskeyのbotアカウントのドライブ容量上限を引き上げておくのを忘れないようにしましょう。
Dockerが動く環境ならどこでも動くと思います。動かし方なんかはGithubのReadmeに載っているので、そちらを参照していただければと思います。ただし、何度も強調している通りXの規約上グレーな行為であることを自覚した上で、自己責任でお願いします。
唯一問題になってくるのがcookieから取得したトークン情報の有効期限です。Devtoolsで確認するとわかりますが、大体半年くらいでトークンの有効期限が切れます。ですので、このbotでは環境変数にdiscordのwebhookURLを入力することで、トークン失効時の401 Unauthorizedを検知して通知を飛ばす機能を組み込んでいます。少し面倒ですが、これはしゃーないかなと思います。
ということで、今回作ってきたmisskey-x-mirror-botの中身の紹介と、開発における苦労話でした。twitter-scraperとmisskey-jsのおおまかな仕様がつかめたのは勿論、コーディングエージェントの凄さを改めて実感できた開発でした。せっかくの環境ですし、もう少し難しいものも作ってみたいなと思ったり。
今回の完成品を使って実際に動かしているアカウントを以下にリンクしておくので、良ければ見てみてください。最後まで読んでくださりありがとうございました。