毎日の星空の様子や天体の位置などをTwitterでツイートするbotを作って2年前から運用しています。
開発した当時にこのブログで紹介するのを忘れていたのでいつか書こうと思っていたのですが、Twitterの経営方針が変わって今後のTwitterのbot運用がどうなるのか不透明で、botがいつ止まってしまうかもわからないので、このタイミングで思い出としてbotの設計を記憶の範囲で書いておきます。
このbotは、天体の位置を計算できるものはツイートを自動生成し、ツイート頻度が低く計算のハードルが高い内容は事前に手動でツイートを作成しています。
以前からこういうのを作りたいと思いながら、計算するプログラムをどう書いていいのかわからなくて挫折していました。しかし、約2年前に改めて調べてみたところ、勢いで計算できちゃったので、運用始めました。
ツイート内容の計算方法
太陽や月、惑星の位置は、NASAのJPL(Jet Propulsion Laboratory)が公開しているDE430というデータから計算しています。このデータは軌道要素ではなく、32日間の期間ごとのチェビシェフ多項式の係数が含まれており、係数から惑星の位置を計算できるようになっています。軌道要素のずれである摂動を考慮済みの位置を直接計算できます。
チェビシェフ多項式については以前に以下の記事を書きました。
チェビシェフ多項式(Chebyshev polynomials)でフィットさせた係数から値を復元 - suzuki-navi’s blog
地球からの惑星や月の相対位置を計算するにはまず地球の位置を計算する必要があります。地球の位置を直接計算するデータはDE430にはありません。かわりに、地球・月系の重心位置、および地球から見た月の位置を計算するためのデータがDE430にあります。この2つから地球の位置を計算することになります。
恒星時は時間の1次関数です。特定の時間における惑星や星座の方位角や高度は、恒星時と東京の経緯度から計算しています。地心視差は考慮していません。本当は月に関しては地心視差を考慮したほうがいいかもしれません。
星座に関するツイートは、あらかじめ原稿を用意していて、恒星時からツイートするタイミングだけを計算しています。
構成
ツイートテキスト生成と自動ツイートの2つの構成です。
ツイートテキスト生成
ツイートテキストを生成するローカルで動かすプログラムです。ツイートテキストとツイート時間のペアの一覧をこのプログラムで生成して、AWSのS3にアップロードします。単一のテキストファイルを生成しています。
プログラムの構成は最初に作ったときから半年ほどかけて少しずつ変わりました。
Ruby
↓
Ruby + Scala
↓
Ruby + Scala + Node.js
↓
Scala + Node.js
↓
Scala
一番最初はRubyだけで実装していました。なぜRubyなのかというと以下の方のgemを利用したかったためです。
Ruby - JPL 天文暦 gem の作成! - mk-mode BLOG
しかし、ツイート内容の柔軟な生成がRubyでは辛かったのと、計算速度が遅かったため、途中で一部をScalaにしました。さらに、自動生成と手動生成をマージするところだけNode.jsで書きました。マージ処理の言語はなんでもよかったのですが、後述の通りPythonもすでに併用していたので、せっかくならばとバラバラな言語で書いてみました。
その後、計算速度改善のためRubyのコードは全部Scalaに置き換わりました。その後、ロジックの柔軟な対応のためにNode.jsでの実装もScalaに置き換えました。
ソースコードは以下にあります。リファクタリングをしようとして中途半端になっており、非常に汚いコードです。手動で作成したツイート内容はテキストファイルで書いてあります。
https://github.com/suzuki-navi/hoshizora_today/tree/main/calc
自動ツイート
AWS Lambdaで動くプログラムです。Twitter APIにアクセスしてツイートします。Pythonで書きました。
EventBridgeで5分に一度Lambdaを起動します。S3からデータを読み取って、その時間帯にツイートすべきテキストがあればツイートします。
https://github.com/suzuki-navi/hoshizora_today/blob/main/tweet/tweet.py
Twitter APIにアクセスするのはtwitterというずばりな名前のパッケージを使っています。
ツイートテキストに出てくる用語集
以下のページにも説明を書いています。
開発当時のブログ記事
このbotを開発するときに書いた関連するブログ記事は以下があります。
- 天体の位置計算に関するメモ
- 実装技術に関するメモ
- AWS CloudFormationのデプロイ済みスタックのテンプレートをYAMLで取得 - Qiita
- Serverless FrameworkとAWS Lambda with RubyでS3アクセス - suzuki-navi’s blog
- Serverless FrameworkとAWS Lambda with Rubyの環境にgemインストール - Qiita
- Serverless FrameworkとAWS Lambda with Python or Node.jsでS3アクセス - suzuki-navi’s blog
- Serverless FrameworkとAWS Lambda with Pythonの環境にpipインストール - Qiita
- 大量の浮動小数点数のデータをRubyからバイナリ出力してJavaで読み込む - suzuki-navi’s blog
- 大量の浮動小数点数のデータをJavaからバイナリ出力してPythonで読み込む - suzuki-navi’s blog
これを見るといろいろな言語で試行錯誤していたようです。当初はRubyで全部書こうとしていたのでAWS LambdaでRubyを動かす方法を調べていたのですが、途中で言語は適材適所と考え直し、AWS LambdaからTwitter APIにアクセスする部分はPythonで書くことにしたのだと思われます。