前回の
🏛️ システムの全体構成(変更後)
GitHub Actionsを用いた「テキスト単発送信」の構成から、Proxmox上のコンテナを用いた「常時待機型の画像対応サーバー」へとアーキテクチャを刷新しました。
入力・送信: PCのローカルに置いた「自作のHTMLファイル」前回のを微修正
中継・実行: Proxmox上のLXCコンテナ内で常時稼働する「Node.js (Express) サーバー」
エンドポイント (各SNS): Bluesky (AT Protocol)、Misskey (REST API)、mixi2 (gRPC API)
ブラウザのHTMLから「画像とテキスト(FormData)」をローカルサーバーのIP宛てにPOST送信し、サーバーがそれを受け取って各SNSの仕様に合わせて画像を処理・一斉アップロードするという流れです。
🛠️ 構築のステップとポイント
1. インフラ環境の準備(Proxmox LXC)
Proxmox上に軽量なLXCコンテナ(Ubuntu/Debian)を構築。
2. Node.js環境とパッケージの準備
コンテナ内にNode.js(v24)をインストール。
プロジェクトフォルダを作成し、以下の主要ライブラリを導入。
express: Webサーバー化するため
multer: HTMLから送られる画像データを受け取るため
dotenv: 各SNSのAPIキーやパスワードを隠しファイル(.env)から読み込むため
sharp: 画像のサイズを最適化(圧縮・リサイズ)するため
3. フロントエンド(送信画面)の改修
PC上のHTMLファイルに <input type="file"> を追加し、画像を選択できるように改修。
送信形式をJSONから FormData 形式に変更。
HTML 例
https://sedate-word-409.notion.site/Node-js-_HTML-357e72820163803e9c79c04409ecf1f5?pvs=73
4. 中継・実行システムの構築と各SNSの「画像の壁」突破
bot.js を「終わったら閉じるスクリプト」から、「ポート3000番でリクエストを待ち続けるWebサーバー」に書き換えました。各SNSへの画像アップロードでは、それぞれの仕様と罠を攻略しました。
🦋 Blueskyの壁(2MB制限)
問題: 高画質な画像(2MB以上)をそのまま送るとAPIに弾かれる。
解決策: サーバー側で sharp を使い、全SNS共通の前処理として「横幅1600px以下にリサイズ&画質80%で圧縮」を実行。これにより安定して2MB以下に抑え込むことに成功。
🥝 Misskeyの壁
仕様: 認証は MISSKEY_TOKEN のみ。画像を「ドライブ」エンドポイントにアップロードし、発行されたファイルIDをテキストと一緒にノート作成エンドポイントへ送るという素直なREST仕様でスムーズに突破。
🟠 mixi2の壁(4ステップ仕様と非公式ライブラリのバグ)
仕様: ①アップロード開始宣言 → ②バイナリ送信 → ③完了ポーリング(状態確認) → ④ポスト作成、という複雑なgRPC仕様。
罠①(バイナリ送信): Node.js標準の fetch を使って巨大なデータを送ると、サイズ不明(または0バイト)の不正なデータとして送られてしまい、mixi2側でIDが即座に破棄される。
解決策①: Node.jsネイティブの https モジュールを使用し、ファイルサイズ(Content-Length)を明示して生データ(Buffer)を直接流し込むことで確実な送信に成功。
罠②(ポーリングのバグ): 非公式ライブラリ(mixi2-js)の「状態確認(Step 3)」機能自体にバグがあり、リクエスト形式の不一致で invalid media_id エラーを起こして自爆する。
解決策②: Step 2の送信完了後、「状態確認APIをあえて使わず、強制的に8秒待機してすぐStep 4(投稿)を実行する」 という強行突破(スキップ)で解決。画像圧縮により処理は1〜2秒で終わるため、これで安全に動作する。
bot.js 例
https://sedate-word-409.notion.site/Node-js-_bot-jp-357e728201638063b044c2c8a291c3a3?pvs=73
こんな感じで同時送信できました。99%Geminiに聞きながらできたので、楽な時代になったような、駄目な時代になったような。
(割と作って満足したので今後どれだけ使うかは謎ですが)各種アカウントはこちらです。