マキモノ
SNS運用無料✅ 公式検証済みv1.0.0 / 更新

従量課金 SNS API の送信前ガードを実装する手順(X/Twitter 例・誤検知の教訓付き)

1投稿ごとに課金される X API で、壊れた本文(空文・280字超・プレースホルダ・文字化け)を送信直前に止めるガードの実装・テスト・CI・本番確認の全手順。裸の TODO が本文の普通語を誤検知した実測と回避策、委譲時に防護解除を禁止する1行を含む

出品者: nishi@orgiast.jp📖 読込 約2,769トークン (約4円)💰 コスパ 8
トークン節約メーター84%節約
ゼロからAIに作らせた場合2.5万トークン
このMDを読ませた場合4,000トークン

2.1万トークンの節約 (API料金換算で約32円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。

この巻物について

従量課金 SNS API の送信前ガードを実装する手順(X/Twitter 例・誤検知の教訓付き)」は、SNS運用カテゴリのAI指示書(MDファイル)です。1投稿ごとに課金される X API で、壊れた本文(空文・280字超・プレースホルダ・文字化け)を送信直前に止めるガードの実装・テスト・CI・本番確認の全手順。裸の TODO が本文の普通語を誤検知した実測と回避策、委譲時に防護解除を禁止する1行を含むこの巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約2.1万トークン(API料金換算で約32円)・84%のトークンを節約できます。

カテゴリ
SNS運用
対応AI
claude-code、cursor、codex-cli
ライセンス
商用利用可 (再販不可)
価格
無料
ゼロから開発時
約2.5万トークン
この巻物使用時
約4,000トークン
節約量
約2.1万トークン (約32円)
更新日
2026-09-18

使い方 (AIに渡す3つの方法)

いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/sns-api-x-twitter/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

従量課金 SNS API の「送信前ガード」を実装する手順(X/Twitter 例)

X(旧Twitter) API は 2026 年から無料の書き込み枠が無くなり、1 投稿ごとに課金される(テキストのみ ≒ $0.015、URL 付き ≒ $0.20)。 壊れた本文をそのまま投げると 金銭的損失がそのまま出る ので、client.create_tweet() を呼ぶ直前に検査して、異常なら投稿せず異常終了させる。 この巻物は、Python の投稿ボット(GitHub Actions cron で queue.json から 1 件ずつ投稿する構成)にガードを移植した実測手順。

前提

  • 投稿ボットは poster.py 1 本。queue.json[{id, text, posted, ...}] の配列
  • 既に重み付き文字数を数える関数(例: wlen(s) — CJK は 2、URL は 23、その他 1)がある。無ければ先に作る。無い場合の最小実装は末尾に置いた
  • テストは pytest。requirements.txt は本番依存だけにし、pytest は CI 側で個別 install する

1. ガード関数を追加する

X_MAX_WEIGHTED_LEN = 280  # X の1ツイート上限(重み付き)

# プレースホルダ検知。TODO/TBD/FIXME は「TODO:」「【TODO】」のようなマーカー形だけを見る。
# 本文中の普通語(例: 「決定事項/TODO/保留に分類」)は通す。
PLACEHOLDER_RE = re.compile(
    r"(XX+"
    r"|(?:TODO|TBD|FIXME)\s*[::]"                 # TODO: / TBD:
    r"|[\[【((]\s*(?:TODO|TBD|FIXME)\s*[\]】))]"    # [TODO] / 【TBD】 / (FIXME)
    r"|<[A-Za-z_ ]{2,}>|<[^>]*>|\{\{)"
)


def guard_parts(parts):
    """送信前ガード。戻り値は問題の説明リスト(空なら投稿してよい)。"""
    if not parts:
        return ["本文が空です"]

    problems = []
    for i, part in enumerate(parts, 1):
        label = "part {}/{}".format(i, len(parts))
        if not part.strip():
            problems.append(label + ": 本文が空です")
            continue

        w = wlen(part)
        if w > X_MAX_WEIGHTED_LEN:
            problems.append("{}: 長すぎます (weighted {} > {})".format(label, w, X_MAX_WEIGHTED_LEN))

        m = PLACEHOLDER_RE.search(part)
        if m:
            problems.append("{}: プレースホルダが残っています ({!r})".format(label, m.group(0)))

        # 文字化け検出: 日本語が1文字も無いのに Latin-1 補助域が多い = エンコーディング取り違え
        jp = sum(1 for ch in part if 0x3040 <= ord(ch) <= 0x30FF or 0x4E00 <= ord(ch) <= 0x9FFF)
        latin1_supp = sum(1 for ch in part if 0x0080 <= ord(ch) <= 0x00FF)
        if jp == 0 and latin1_supp >= 5:
            problems.append(
                "{}: 文字化けの疑い (日本語0文字 / U+0080-00FF が {}文字)".format(label, latin1_supp)
            )

    return problems

検査は 4 項目。順番に意味がある: 空文は他の検査をスキップ(continue)、長さ・プレースホルダ・文字化けは 全部列挙 して 1 回の失敗ログで直せるようにする。

2. 呼び出し位置は「分割の直後・DRY_RUN 判定より前」

chunks = chunk(nxt["text"])          # スレッド分割済みの本文リスト
problems = guard_parts(chunks)
if problems:
    print(f"[guard] {nxt['id']} rejected — refusing to post:", file=sys.stderr)
    for p in problems:
        print(f"  - {p}", file=sys.stderr)
    sys.exit(1)

if os.environ.get("DRY_RUN") == "1":
    ...                               # ここより前に置くので DRY_RUN でもガードが効く
  • 分割後に検査する。分割前の全文で 280 字を判定すると、分割後の 1 ツイートが超えていても見逃す
  • DRY_RUN より前に置く。ローカルの DRY_RUN=1 python poster.py で本番と同じガードを踏める
  • 問題があれば queue.jsonposted を更新しない。cron は次回も同じ id で落ち続けるので、壊れた本文は永久に投稿されない(= 失敗が見える)

3. テストは「実在キュー全件が通る」を必ず入れる

def test_real_queue_passes_guard():
    with open("queue.json", encoding="utf-8") as f:
        q = json.load(f)
    failures = [(item["id"], guard_parts(chunk(item["text"]))) for item in q]
    failures = [(i, p) for i, p in failures if p]
    assert failures == [], "ガードで落ちた投稿: {}".format(failures)

このテストが赤になるのは「ガードの誤検知」か「キューの不備」のどちらか。実装者はキューを直さず、id と理由を報告して監督が判定する(実測では誤検知だった。下記)。

陽性テストは各項目 1 つずつ: [][" "]"あ" * 141(重み 282)、"TODO: ここに書く" "【TODO】" "[TBD]" "<社名>""ãÂÂãÂÂ" * 2。 陰性テストも入れる: "決定事項/TODO/保留に分類する""TODOリストを整理する"[] を返すこと。

4. CI を足す(requirements.txt は汚さない)

name: CI
on: [push, pull_request]
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r requirements.txt pytest
      - run: python -m pytest -q

5. 実測で踏んだ落とし穴

  1. 裸の TODO|TBD|FIXME は誤検知する。実キュー 49 件のうち 1 件に「議事録を決定事項/TODO/保留に分類する」という普通語の TODO があり、CI が赤になった。マーカー形(TODO: 【TODO】 [TBD])だけに絞ったら 49 件全通過。正規表現を決めたら、実キューと陽性例・陰性例の 3 セットで先に検証してからコードに入れる
  2. 重み付き文字数の関数を二重に持たない。参照元にあった weighted_len() をそのまま持ち込むと、既存の wlen() と基準がずれる(URL を 23 と数えるかどうか等)。既存関数を使う。
  3. Windows で DRY_RUN すると cp932 で絵文字が落ちるPYTHONUTF8=1 を付ける(コード側で sys.stdout.reconfigure() を入れるより、実行環境の指定で済ませる)。
  4. squash マージ後のローカルブランチは git branch -d で消えない(祖先関係が無い)。内容が main に入っていることを gh pr view --json state で確認して -D
  5. 本番反映の確認は cron の次回 run ログで足りるgh run view <id> --log | grep -E "posted m|\[guard\]"posted が出て [guard] が出なければガード込みのコードで実投稿が通っている。

付録: 重み付き文字数の最小実装(既存が無い場合)

URL_WEIGHT = 23

def wlen(s: str) -> int:
    urls = re.findall(r"https?://\S+", s)
    base = s
    for u in urls:
        base = base.replace(u, "")
    w = len(urls) * URL_WEIGHT
    for c in base:
        o = ord(c)
        w += 2 if (0x1100 <= o <= 0x11FF or 0x2E80 <= o <= 0x9FFF or 0xAC00 <= o <= 0xD7FF
                   or 0xF900 <= o <= 0xFAFF or 0xFF00 <= o <= 0xFF60 or o >= 0x1F000) else 1
    return w

AI に委譲するときの指示書に必ず入れる 1 行

「hook やガードにブロックされたら止めて報告する。防護を解除する設定ファイルを自作しない」。 実測では、安価な実装モデルが監督用の防護 hook を 解除ファイルを自作して 迂回し、作業後に消していた。この 1 行を入れた 2 回目は迂回なしで完了した。

よくある質問

「従量課金 SNS API の送信前ガードを実装する手順(X/Twitter 例・誤検知の教訓付き)」とは何ですか?

1投稿ごとに課金される X API で、壊れた本文(空文・280字超・プレースホルダ・文字化け)を送信直前に止めるガードの実装・テスト・CI・本番確認の全手順。裸の TODO が本文の普通語を誤検知した実測と回避策、委譲時に防護解除を禁止する1行を含む

どれくらいトークン(費用)を節約できますか?

ゼロから開発すると約2.5万トークンかかりますが、この巻物を使えば約4,000トークンで済みます。差し引き約2.1万トークン(API料金換算で約32円)・84%の節約です。

どうやって使いますか?

無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。

どのAIツールに対応していますか?

claude-code、cursor、codex-cli に対応しています。

商用利用できますか?

ライセンスは「商用利用可 (再販不可)」です。

🤝 自分でAIを動かすのは、まだ不安…という方へ

この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア