agentsclimarketplace

Discord gateway resume dedup

Skill bokuwalily/claude-code-skills/skills/discord-gateway-resume-dedup

自作Discord gateway bot(WebSocket直叩き)が「前の指令を実行中」等を指令してないのに勝手に出す/同じ指令が二重実行される時。原因はRESUME再配信の未dedup。From its SKILL.md

Install
npx -y skills add bokuwalily/claude-code-skills --skill discord-gateway-resume-dedup

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

2.7 KB, 893 tokens by cl100k_base, as published. Nobody here has run it

Procedure

Discord gateway を WebSocket で直接叩く自作bot(例 ~/.discord/bridge.mjs)で、 指令を出してないのに過去の応答/⏳busyメッセージが湧く、または1回の指令が二重実行される時。

原因

Discord gateway はハートビート切れ→自動RESUME(op 6)のたびに、 前回ack済み seq 以降のイベントを再配信する。MESSAGE_CREATE の重複排除が無いと、 過去メッセージのリプレイで handleMessage が再実行され、亡霊応答や二重実行になる。 ゾンビ再接続(if (!acked) ws.close(4000))を持つbotは特に頻発する。

修正(2点)

グローバルに起動時刻と既処理IDセットを置く:

const BOOT_TIME = Date.now();                 // これより前のメッセージはリプレイ
const seenMsgs = new Set();                   // 処理済みID(再配信の重複排除)
const snowflakeTime = (id) => Number((BigInt(id) >> 22n)) + 1420070400000;

メッセージハンドラ冒頭(オーナー/ch判定の直後)でガード:

if (m.id) {
  if (snowflakeTime(m.id) < BOOT_TIME) return; // 起動前=RESUMEリプレイ。無視
  if (seenMsgs.has(m.id)) return;              // 同一メッセージ再配信。dedup
  seenMsgs.add(m.id);
  if (seenMsgs.size > 500) seenMsgs.delete(seenMsgs.values().next().value);
}

反映

launchd管理なら kickstart で再起動(killでもKeepAliveが拾う):

launchctl kickstart -k gui/$(id -u)/<label>   # 例 com.lily.discord-bridge

Pitfalls

  • snowflake→ms変換は (BigInt(id) >> 22n) + Discord epoch(1420070400000)。22bitシフト必須。
  • seenMsgs は無限に貯めない。上限を切ってFIFO削除(上の500件ローテ)。
  • busy/in-flightフラグの解除は必ず finally に置く。runClaude等がハングするとフラグが残り、 dedupとは別経路で「実行中」が居座る。dedup入れても解除漏れは別途潰す。
  • close時の再接続は resume可能コード(4000,4001,1006等)で resume、不可なら fresh。 dedupはどちらの経路でも効く。

Verification

  • 修正後 node --check bridge.mjs で構文OK。
  • 再起動しログに READY as <bot> が出る(bridge.out.log)。
  • 一定時間放置→再接続が起きても、過去指令が再生されないこと。
  • 1指令=1応答(▶️受付→✅完了が1回ずつ)で二重化しないこと。

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most hr recruiting skills give in 893 tokens

Counted across 356 of the 357 authors here whose files we hold, read 2026-08-07

  • Quantify achievements with specific metricsin 14 of 356, across 6 files
  • Keep the resume under two pagesin 14 of 356, across 6 files
  • Request the full job description if not providedin 12 of 356, across 4 files
  • Extract keywords and prioritize job requirementsin 12 of 356, across 4 files
  • Stop and ask for clarification if required inputs are missingin 12 of 356, across 5 files
  • Map candidate experience to job requirementsin 11 of 356, across 3 files
  • Ask if the user wants adjustmentsin 11 of 356, across 3 files
  • Provide strengths and gap analysis after the resumein 10 of 356, across 2 files
  • Request candidate background details if not providedin 10 of 356, across 2 files
  • Format experience bullets as action verb plus resultin 10 of 356, across 2 files
  • Ask for missing inputs before startingin 10 of 356, across 9 files
  • Use exact job description terminologyin 9 of 356, across 1 file

Said here and by no other author read

  • ignore messages older than boot time
  • skip already processed message ids
  • store processed message ids in a bounded set
  • convert snowflake ids using a 22-bit right shift
  • clear busy flags inside a finally block
  • use resumable close codes for reconnections

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.