v2 — @cc-discord-framework/core
このサイトと main のコードが説明する現行版。公式プラグインも 同じ @cc-discord-framework スコープで公開されています。
bun add @cc-discord-framework/core導入手順を確認する →CC / CHACCHANv2.0.0 · npm 公開中
クラスを置く。フレームワークが見つけ、登録し、つなぐ。
Discord Bot の起動までを、規約と型に任せられます。
bun add @cc-discord-framework/coreauto-discovery — 登録コードを書かずに、配置だけで Bot が組み上がる
Project status / Updated 2026-08-25
公開中の v2(@cc-discord-framework/core)と旧 npm パッケージ v1 を混同しないための、現在地をまとめました。
詳しい状況とサポート方針 →
このサイトと main のコードが説明する現行版。公式プラグインも 同じ @cc-discord-framework スコープで公開されています。
bun add @cc-discord-framework/core導入手順を確認する →旧パッケージ / legacy · 2026-05-10
スコープなしの旧パッケージは旧世代の 1.x です。v2 とは API 互換ではないため、新規導入では使わないでください。
npm の v1 を確認する ↗v1.0.5 Release ↗Maintained by
熊の耳にした2つの C は CHACCHAN から。CHACCHAN が設計・実装・ ドキュメントを保守する個人メンテナンスの OSS です。公開コードと 運用リファレンス Bot を基準に改善しています。
GitHub プロフィール ↗01 — 設計思想
発見・登録・ルーティング・型付け — Bot を書くたびに繰り返してきた「いつもの配線」を、規約と型で肩代わりします。
commands/ に置いたクラスが、そのままスラッシュコマンドになる。 import も登録リストも書きません。名前はクラス名から導出 (UserInfoCommand → /user-info)。
どのコンポーネントからも this.services.audio / this.services.ai / this.services.ui。 宣言マージで型も通ります — 手動のジェネリクス指定はありません。
export * from "discord.js" — 全 API を再エクスポートし、Client は discord.js の Client そのもの。 これまでの知識も、エコシステムも、すべてそのまま使えます。
02 — discord.js との関係
cc-discord-framework は discord.js 14 の全 API を再エクスポートします (export * from "discord.js")。 柔軟性はそのままに、その上へ規約と型の構造を足す — 同じ discord.js でも、 書く量はこれだけ変わります。
discord.js のみ定義・登録・ルーティングを手で配線
cc-discord-frameworkファイルを置くことが、そのまま登録
// src/index.ts — エントリポイントはこれだけ
import { Client, GatewayIntentBits } from "@cc-discord-framework/core";
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
await client.login(); // トークンは DISCORD_TOKEN 環境変数から自動使用
// src/commands/PingCommand.ts — 置くだけ。登録・同期は自動
import { Command, type ChatInputCommandInteraction } from "@cc-discord-framework/core";
@Command.define({ description: "Botの応答速度を確認します。" })
export class PingCommand extends Command {
override async chatInputRun(interaction: ChatInputCommandInteraction) {
await interaction.reply(`Pong! ${this.client.ws.ping}ms`);
}
}
interaction は discord.js の型そのもの。2つ目のコマンドは ファイルをもう1枚置くだけで、左のコードのように登録行や分岐が増えることはありません。
03 — 主要機能
コアが持つのはサービス・コマンド・リスナー・Precondition だけ。それ以外は、同じ仕組みの上にプラグインとして積み上がります。
@Command.define({...}) は TC39 標準デコレータのみ。 experimentalDecorators も reflect-metadata も不要です。 デコレータは宣言し、ローダーが実行する — 厳密に分離されています。
createClient() が src/config/ を読み、1関心1ファイルの 設定を合成。plugins は priority 順に連結、intents は 合併(union)されます。
プラグインは機能ではなくコンポーネント種別ごと追加できます。utils は tasks/、music は resolvers/+providers/、 ai は ai/ — すべて Public API だけで実現。
ユーザーに見える文言・色・上限は、すべてただの既定値。 変更できないハードコードは設計ルールとして存在しません。 Bot の見せ方は Bot が決めます。
Bun 1.4+ 専用。TypeScript をそのまま実行するので、開発にビルド工程は ありません。500件を超える自動テストを bun test でまとめて実行できます。
リスナーのイベント引数、ストア参照、this.services.*、 Precondition 名 — 宣言マージにより、手動のジェネリクス指定なしで型が通ります。
04 — コード
以下はすべて、リポジトリ同梱の公式リファレンス Bot(client/)で実際に動いているコードです。
オプションはデコレータで宣言し、本体は this.services.ai.reply() の1行。defer・ストリーミング表示・ 長文の分割・失敗時の表示は、すべてサービス側の責務です。
AskCommand → /ask)import {
ApplicationCommandOptionType,
Command,
type ChatInputCommandInteraction,
} from "@cc-discord-framework/core";
@Command.define({
description: "AIに質問します(会話履歴は使いません)。",
options: [
{
type: ApplicationCommandOptionType.String,
name: "prompt",
description: "聞きたいこと",
required: true,
},
],
})
export class AskCommand extends Command {
override async chatInputRun(interaction: ChatInputCommandInteraction) {
// defer・ストリーミング表示・長文の分割・失敗時の表示は reply() の担当。
await this.services.ai.reply(interaction, {
prompt: interaction.options.getString("prompt", true),
});
}
}
コマンドストアを走査して一覧を作り、this.services.ui の テーマ済み埋め込みと paginate() でページ送りに。 他のコンポーネントを import する行は1本もありません。
this.container.stores — 全コンポーネントへ型付きでアクセスimport { chunk, paginate, type Page } from "@cc-discord-framework/utils";
import { Command, type ChatInputCommandInteraction } from "@cc-discord-framework/core";
const PAGE_SIZE = 20;
@Command.define({ description: "コマンド一覧を表示します。" })
export class HelpCommand extends Command {
override async chatInputRun(interaction: ChatInputCommandInteraction) {
const lines = this.container.stores
.get("commands")
.map((command) => `**/${command.name}** — ${command.description}`)
.sort();
const pages: Page[] = chunk(lines, PAGE_SIZE).map((body, page, all) =>
// utils プラグインの this.services.ui — 色は Bot 全体のテーマから。
this.services.ui
.info(body.join("\n"))
.setTitle("コマンド一覧")
.setFooter({ text: `${page + 1}/${all.length}ページ・全${lines.length}コマンド` }),
);
await paginate(interaction, { pages, ephemeral: true });
}
}
tasks/ は utils プラグインが追加するコンポーネント種別。 コアの commands/ と同じように、置くだけで定期実行が始まります。
every: "5m" — 実行間隔も宣言的にrunOnStart: trueimport { ActivityType } from "@cc-discord-framework/core";
import { Task } from "@cc-discord-framework/utils";
/** 公式 utils プラグインが追加する Task 種別のコンポーネント。 */
@Task.define({ every: "5m", runOnStart: true })
export class PresenceTask extends Task {
override run() {
this.client.user?.setPresence({
activities: [{ type: ActivityType.Playing, name: "/help" }],
});
}
}
intents は設定ファイル間で合併(union)されるので、音楽にしか 要らない GuildVoiceStates は music の設定に置けます。 音楽をやめるときはこのファイルを消すだけ — 要らなくなった intent も一緒に消えます。
plugins は priority 順に連結 — 依存の向きで順序を制御createClient() が config/ を合成import { defineConfig, GatewayIntentBits } from "@cc-discord-framework/core";
import { music } from "@cc-discord-framework/music";
import { musicSources } from "@cc-discord-framework/music-sources";
import { env } from "./_env.js";
export default defineConfig({
priority: 50,
intents: [
// 音楽再生に必要(ボイスチャンネルの出入りを追うため)。
GatewayIntentBits.GuildVoiceStates,
],
plugins: [
// キュー・再生制御の this.services.audio(/play などは src/commands/)。
music(),
// YouTube と SoundCloud を音源として追加(music より後に置く)。
musicSources({
soundcloud: { clientId: env.soundcloudClientId },
}),
],
});
src/ai/ に置いたクラスは、モデルが呼び出せるツールになります。 中では this.services.audio(music プラグイン)がそのまま使える — プラグイン横断の合成です。/chat で「いま何の曲?」と聞くと、 モデルがこのツールを呼びます。
guildOnly: true で DM からの呼び出しを制限import { AiTool, type AiToolContext } from "@cc-discord-framework/ai";
import { z } from "zod";
const input = z.object({
キュー: z.boolean().optional().describe("待機中の曲を題名の一覧で返すかどうか"),
});
@AiTool.define({
description:
"このサーバーで再生中の曲と、待機中の曲の状況を返します。音楽の再生状況を聞かれたら使ってください。",
inputSchema: input,
// 再生キューはサーバー単位なので、DM からの呼び出しでは使わせない。
guildOnly: true,
})
export class NowPlayingTool extends AiTool<z.infer<typeof input>> {
override execute({ キュー = false }: z.infer<typeof input>, context: AiToolContext) {
const queue = context.guildId === null ? null : this.services.audio.queue(context.guildId);
if (!queue?.current) return { 再生中: null, 待機中: 0 };
return {
再生中: { 題名: queue.current.title, 演者: queue.current.author },
待機中: キュー ? queue.tracks.map((track) => track.title) : queue.tracks.length,
};
}
}
05 — プラグイン
プラグインが提供するのは、コンポーネント種別の自動ロード・サービス・ イベントの3つだけ。コマンドは登録しません — /play も /ask も Bot の機能なので、src/commands/ に自分で書きます。文言も見せ方も、Bot が決める。
そして種別は横断して合成できます。src/ai/ のツールの中で this.services.audio が普通に動く — 「いま流れている曲」を AI が答えられるのは、この合成のおかげです。
独自のデコレータ・ディレクトリ・ライフサイクルを持つ新しい種別を、 Public API だけで丸ごと追加できます。公式プラグインも、 この同じ拡張点の上に立っています。
置く場所が、そのまま役割。プラグインを入れると、読めるディレクトリが増える。
06 — 公式プラグイン
公式プラグインはそれぞれ独立したパッケージ。すべて npm の @cc-discord-framework スコープで公開されていて、使う分だけ bun add で足せます。
@cc-discord-framework/utilsテーマ済み埋め込みの this.services.ui、確認ダイアログの confirm()、ページ送りの paginate()、 定期実行の tasks/ 種別、時間・文字列の整形ユーティリティ。
@cc-discord-framework/musicthis.services.audio で解決・キュー・再生制御。 コマンドは登録しない設計 — /play の文言も見せ方も Bot 側が決めます。音源はプロバイダー機構で差し替え可能。
@cc-discord-framework/music-sourcesmusic プラグインに YouTube と SoundCloud を音源として追加。 Bot 側の /play から検索語や URL を同じ Resolver へ渡せます。yt-dlp と ffmpeg を使う音源処理を、 本体から分離した独立パッケージです。
@cc-discord-framework/aiVercel AI SDK ベース。this.services.ai.reply() が defer・ストリーミング・分割まで引き受け、ai/ に置いた クラスはそのまま LLM のツールになります。
07 — はじめる
最小の Bot まで3ステップ。ボイラープレートの生成も、ビルドの設定もありません。
discord.js は同梱・再エクスポートされるので、個別にインストールする 必要はありません。ランタイムは Bun 1.4+ だけ。
src/index.ts に Client を作って login()。トークンは DISCORD_TOKEN 環境変数から自動で使われます。
src/commands/ にコマンドのクラスを1枚。ビルド工程なしで bun run src/index.ts — もう /ping が動いています。
bun add @cc-discord-framework/core
bun run src/index.ts
Built in Japan
cc-discord-framework は日本発のオープンソースです。 ドキュメントもコードコメントもエラーメッセージも、翻訳ではなく 最初から日本語で書かれています。MIT License。