Featured image of post 本格雑談くちをひらくの更新通知Botを全面的に作り直していた話

本格雑談くちをひらくの更新通知Botを全面的に作り直していた話

以前作った本格雑談くちをひらくの更新通知Botをリファクタリングしていたので、何をしたかをまとめた記事です。

2023年に リスナーが勝手にPodcast番組の更新通知Botを作って遊んだ話 という記事を投稿しました。

記事の内容をざっくり3行で説明します。

  • Podcast番組「本格雑談くちをひらく」の公式アカウントからの更新通知が途絶えた。
  • 原因はIFTTTとXの連携有料化
  • リスナーが勝手にBotを自作して動かし始めたら公式的に黙認された

このBotは2023/06/26の回から正式稼働を始めました。

それから1年が経過し、先日2024/06/26の回で1周年についてのメールを取り上げていただきました。

番組内のメールでも読まれましたが、実はリリースからの1年の間で、Botの中身をすべて作り直しています。

今回は、稼働から1年の間の変化と、現状のプログラムの仕様についてまとめておきます。

(本当はこの記事は2024/07に出す予定でした。気づいたら1年経過していました。)

ちなみに、僕の所属するサークルが出した SWSD REPORT VOL.3.5 でも取り上げています。ご興味があればぜひ。

フォーマット変更

リリース直後は、元々使われていたフォーマットを踏襲していました。

「 [YYYY/mm/dd HH:MM up!] 『タイトル』 podcast→<AppleのPodcastのリンク> Web→<omny.fmの全体リストページのリンク> #くちをひらく <omny.fmの当該回へのリンク> @yoshidahisanori @eriko_co_log 」

ただ、リスナー視点としては流石に1行でまとまっていて見づらかったこともあり、変更を考えます。

すると、めちゃくちゃありがたいことに、吉田さんから受け入れていただけます。

フォーマットについてはこれを導入し、いまに至っています。改行を入れ、AppleのPodcastのリンクを消してVoicyへのリンクを追加、といったところですね。

アイコンを描いていただいてしまった

導入当初、公式アイコンが緑っぽいということで、真緑のアイコン(mspaintで15秒位で作った)にしていましたが、番組のふつおた枠に「なにか寂しい気がするのでもうちょっと華やかにしてみたい。なにか良い案等あれば」といったメールを送ったところ、

なんと吉田尚記さんと中村繪里子さんに手書きでアイコンをつくっていただいてしまいました。非公式で、公式に黙認していただいている立場にも関わらずありがたすぎるお話です……。大切に使わせていただいています。

更新漏れとの戦い

くちをひらくでは、初回リリースから1年間で数回、想定外の更新が数度発生しました。

1日2回の更新

例えば更新設定漏れによる一括投稿では、2023/07/28に、2023/07/26日分と2023/07/27分が同時に更新されたときの話です。(注: ディレクターの石川さんを責めている意図はまったくありません。)

このときの更新は7/28の0時頃に2つ更新されました。このとき、当時のBotは「スクリプトが実行されたタイミングでRSSから取得できたアイテムの最新のものがその日の日付の更新だった場合に、それをツイートする、という動作なので、0時に2つ投稿されたあとにスクリプトだけを実行してしまうと、7/27予定分のみがツイートされてしまいます。

そのため、7/26分を手でツイートし、7/27分をスクリプトで投稿する、といった運用を行いました。(手でツイート、普通にiPhoneでコピペで作っていました。(たまたまPCが手元になかった))

マチ★アソビ起因の突発更新

2023年の秋のマチ★アソビでは、吉田さんと中村さんが徳島の居酒屋で録音された突発的な更新がありました。Botは17時台以外の更新を想定していなかったので、Voicyアプリに来た更新通知で更新を認識し、手動でスクリプトの実行をしていました。

この後も2回ほどあった記憶がありますが、適宜叩いていた気がします。

これが結構悔しいのです。追いつけず人間が出てきて作業した、ということにとても悔しさを感じました。ただ、回数は多くない事象でもあるので、このときは「発生したら発生したで考えよう」という気持ちで一旦改修を見送ります。

BlueskyへのBot

発端

2024年2月、ある意味ではTwitter対抗となるBlueskyが招待制を廃止し、一般開放が行われました。

一時期は僕のTLでも大きくBluesky移行の流行があったのを覚えています。結果としてそこまでの大移動はありませんでしたが、少なくともThreads向けにBotを作るよりは明らかに価値があるのではないか? (本音: ただ作ってみたい)、ということで、Blueskyへの完全非公式なくちをひらくBotの作成を考え始めます。

考えているだけでは良くないので、Blueskyへの投稿機能をBotへ実装し始めます。

ついでにGoへ移行する

Blueskyへの投稿をPHPで行えないか模索してみましたが、ライブラリがあるにはあるも使いづらい、自分で作るにはちょっとめんどくさい、という障壁が立ちはだかります。上手いこと手軽にBlueskyへの投稿ができるものはないか、と模索していると、

mattn/bsky を発見します。mattnさんのGo製Blueskyクライアントですね。

当初はこれをPHP側から僕の盆栽システム開発伝統のshell_execで叩こうと考えましたが、

深夜テンションになり、Goで全部書き直し始めてしまいます。

2022年以降、業務でGolangに触れるようになり、ある程度GoでCLIプログラムがかけるようになっていたこともあり、せっかくならその知見を活かしたいという気持ちもありました。

ただ、後述の通り、Bluesky対応やGoでのリファクタリングをした結果、正直コード量がかなり過剰になってしまいました。流石にどうしようもないのですが、今でもちょっと気になっているところではあります。

まずGoからTwitter API v2使っての投稿のテストからしていましたが、当時のテストポストは消してしまっていたようです。

その後クソみたいなコミットログを積みながら、

二晩でリファクタリングしたGo製のBotの本番投入を決定します。

本番だけど完全公式じゃないからできる技ではありますね。

結果としてTwitterへの投稿とBlueskyがうまく行きました。これは本当に安心した記憶があります。いくら自分のアカウントでテストしていたとはいえ、本番動作では吉田さんと中村さんにメンションが飛びます。一応は間違った内容を出してはいけないので、かなりの緊張がありましたが、無事にPHP→Goのリファクタリングに成功しました。

このPHP→GoのリファクタリングとBlueskyへの投稿機能だけを付けたものは、 Release v1.0.0 · mikuta0407/kuchihira-bot として公開しています。

更新検知ロジックの改修 その1

1日に複数回更新されることを考えると、既存の「最新のもののみを見る」というフローでは失敗します。

そこで、前回投稿したものを記録しておき、差分を確認して投稿するように変更することを決意します。 (厳密に言うと、元々ローカルに状態を持ちたくないという気持ちがあり実装していなかったという経緯があるにはありますが、流石にここまでくると無理なことに気づきました。)

改修方針としては、

  • 前回投稿時のUUIDをファイルシステムに記録する
  • RSSを取得し、最後のUUIDより後に存在しているものを取得する
  • 存在していた場合はそれらを投稿する

というものになります。

実装ロジックは割とすぐ思いついたので、2時間くらいで改修ができました。

ところが改修はしたものの、記録ファイルの読み込み周りの設定を盛大に間違えた結果、間違ったものが投稿されてしまいました。まぁ開発にミスは付きもの……。ごめんなさい。

この後なんとか修正して、検知ロジックを日付ベースではなく差分ベースでみるように完全に変更できました。

ただ、この時点ではBotは17時にcronによって起動する従来の手法をそのまま採用しています。そのため、17時更新以外にはリアルタイム追従ができません(例えば内部的なタイムアウトである2時間を過ぎた後は更新されても検知できない。翌日17時にまとめて検知される)。

更新検知ロジックの改修 その2

ここまできたら、マチ★アソビのときのような例外的な更新や、17時から2時間経過するまで以外でも更新されたものをリアルタイムで拾い、元のIFTTT経由での投稿に近い利便性を取り戻したくなります。

そう、cronで17時に起動させるのではなく、常にデーモンとして動かすように変えるのです。

結果としてまた勢いで実装しました。後ほど説明はしますが、20秒ごとにRSSを掘りに行って差分をチェックし続けるような動作です。

10日ほど運用し、無事に動いたので(不具合出さずに動いて本当によかった)、mergeしてv1.2.0としてリリースしました。
変更の際、従来の単発実行用の処理も残したので、若干コードが汚くなったのが心残りではありますが、単発実行機能を消す気はないのでいつか考えます。

ちなみに、これは詳しい検証をしていないので体感ですが、RSSを取得し差分を確認といった処理が明らかにPHPで記載していたときよりも高速化しています。ここは言語特性の差だとは思いますが、速度差を実感できたのは面白かったです。

v1.2.0以降のデーモン動作に関しての説明

デーモンモードを追加した現在のソースコード自体は mikuta0407/kuchihira-bot v1.2.1 に置いてあります。 (v1.2.0とv1.2.1の違いはREADME.mdのため、動作に関してはv1.2.0と同一です。)

ここからは現状のGo製のkuchihira-botのデーモンモード動作について説明します。単発実行の挙動や、ここでは本質ではないBluesky周りについては割愛します。

パッケージの役割

  • cmd
    • cobraでサブコマンドを実装するための部分。サブコマンドは以下
      • post: 単発実行用(説明割愛)
      • login: Blueskyログイン
      • daemon: デーモンモード実行
  • internal
    • core
      • メインロジックの制御
    • config
      • jsonで保存する各種設定情報の読み取り
    • rss
      • RSSの取得をしてパースを行う
    • twitter
      • Twitterへの投稿
    • bsky
      • BlueSkyのログイン(トークン取得)、投稿、URLカード展開
    • discord
      • 管理人がDiscordで処理結果通知を得るためのWebhook送信

パッケージの依存関係

パッケージの依存関係は以下のようになっています。

  • main
    • cmd
      • internal/bsky (BlueSkyログイン用の依存)
        • internal/config
      • internal/core
        • internal/config
        • internal/discord
          • internal/config (構造体のみの利用)
        • internal/rss
          • internal/config (構造体のみの利用)
        • internal/bsky
          • internal/config (構造体のみの利用)
        • internal/twitter
          • internal/config (構造体のみの利用)

デーモン実行時の動作概要

  • systemctlによってkuchihira-bot daemonが叩かれ起動
  • core.init()によってRSS取得先、連携先に必要なトークン等の情報、使用URL等の読み込みが行われる
  • core.DaemonStartが呼ばれ、常駐スタート
  • 以下の処理が20秒のsleepを挟みながら繰り返し行われ続ける
    • 前回投稿したGUIDを取得
    • RSSの取得先URLから全アイテムを読み出す
    • 前回投稿時のGUIDと、最新アイテムのGUIDが異なる場合、最新アイテムから順番に確認して、前回投稿のGUIDと一致するまで差分アイテムを取得
    • 差分として出てきたアイテムに対してシーケンシャルにそれぞれTwitterとBlueskyへ投稿処理
    • 最後に投稿したアイテムのGUIDをファイルシステムに記録

Omny.fmに対してずっとアクセスし続けていることになりますが、こればかりは許してもらいたいところです。

余談

実は元々公式アカウントが使っていたIFTTTでの告知は、Omny.fmのRSSの更新から20〜30分ほど遅延しての投稿が行われていました(VoicyやSpotifyアプリの通知のほうが早かった)。これは単純にIFTTTのRSSをポーリングする時間の頻度が細かくないことによるものと思われます。PHP時代からではありますが、確実に数十秒間隔でRSSのポーリングを行うため、RSSが更新された直後(最大遅延は現状20秒)に投稿ができるようになり、毎日17:01~17:03の間にはTwitterへ更新通知が飛ばせるようになっています。IFTTTのRSSポーリングと比べてはいけないのはわかった上ですが、ちょっとした自慢ではあります。

突貫で作ったBotがなんだかんだ安定運用していて、安心して過ごしています。公式黙認という立場ですが、今後も安定運用目指していきます。趣味の中で起きる本番運用というものは基本的に自分か友人等の身内のみで終わるものが多く、比較的広い範囲に影響する運用というものは出会うことが出来ません。運用と言っても基本は放置しているだけですが、やりがいは大きいです。楽しくBot運営をやっていますので、もしこの記事を読んで興味を持ってくださった方は、「本格雑談くちをひらく」、ぜひ聴いてみてください!

Licensed under CC BY-SA 4.0
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。