@ copilotkit/ channels- discord (英语)
这个 解析器 PlatformAdapter 页:1 @copilotkit/channels它连接一个
对任何AG-UI代理人的不协调应用:通过不和.js(Gateway), Egress
的V2组件 @copilotkit/channels-ui JSX词汇,加上文本
流出,不透明-id相互作用,以及HITL.
你曾经将你的UI写成JSX(@copilotkit/channels-ui和驱动机器人与
@copilotkit/channels;这个包是唯一与Discord对话的.
适配器保留自己的Discord证书(botToken 编号 : appId /.) ——在
频道运行在 copilotKit 智能配置中的管理路径
CopilotRuntime (免费计划可用),它开始并拥有频道的
生命周期。 在SDK原始版上建造和操作自己的频道跑道
也是一条支持路径.
安装
pnpm add @copilotkit/channels-discord @copilotkit/channels @copilotkit/channels-ui
快速启动
import { createChannel } from "@copilotkit/channels";
import {
discord,
defaultDiscordTools,
defaultDiscordContext,
} from "@copilotkit/channels-discord";
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
const bot = createChannel({
identifyUser: "platform",
name: "support-bot", // project-unique Intelligence Channel name
adapters: [
discord({
botToken: process.env.DISCORD_BOT_TOKEN!, // Bot token — Gateway + REST
appId: process.env.DISCORD_APP_ID!, // Application ID for command registration
guildId: process.env.DISCORD_GUILD_ID, // Optional: instant guild-scoped dev commands
}),
],
agent: (threadId) => makeAgent(threadId),
tools: [...defaultDiscordTools, ...appTools], // lookup_discord_user + your tools
context: [...defaultDiscordContext, ...appContext], // tagging/formatting/thread guidance
commands: [
{
name: "triage",
description: "Summarize the thread and propose issues.",
async handler({ thread, text }) {
await thread.runAgent({ prompt: `Triage: ${text}` });
},
},
],
});
bot.onMention(({ thread }) => thread.runAgent());
// The runtime owns the channel's lifecycle — there is no `bot.start()`.
const runtime = new CopilotRuntime({
intelligence: new CopilotKitIntelligence({
// apiUrl and wsUrl default to cloud-hosted CopilotKit Intelligence — override
// both together only for a self-hosted deployment.
apiKey: process.env.CPK_INTELLIGENCE_API_KEY!, // free tier available
}),
channels: [bot],
});
// Creating the listener starts the Channel's connection.
const listener = createCopilotNodeListener({ runtime });
// Optional: await that activation so a broken config fails startup loudly.
await listener.channels.ready(); // listener.channels.stop() tears it down
discord(opts) 返回 a DiscordAdapter。适配器通过
Discord Gateway (WebSocket)——不需要公共URL. 预过滤器
进入转弯时,机器人应该回答(以盾形通道和
模式,所以一个 onMention 处理者负责大多数使用案件.
必需的嵌入
| 瓦尔 | 目的 |
|---|---|
DISCORD_BOT_TOKEN | 用于 Gateway 登录和 REST 呼叫的 Bot 令牌 . |
DISCORD_APP_ID | 注册斜线命令时使用的应用程序 ID . |
DISCORD_GUILD_ID | (备选) Dev中即时每盾命令注册的Guild ID. |
全局命令 (无)
guildId在~1小时之内通过Discord传播. 带宽命令 (与guildId(d) 立即登记——使用它们 开发期间和转向全球生产.
特权意图
适配器请求 两个 特权网关意图—— MessageContent 财务报告和审定财务报表
GuildMembers两者都必须在
Discord 开发者门户
(您的应用程序 ) 或 Gateway 登录为
拒绝.
- 信件内容意向 ——没有它,Gateway将发送信息与 一个空
content字符串和bot无法读取用户写的东西 .- 服务器成员意图 (单位:千美元)
GuildMembers)——背部成员搜索,其中 权限lookup_discord_user编号 :thread.lookupUser没有它 搜索失败, 登录被拒绝 .
反应意图和部分
适配器也请求 GuildMessageReactions 并实现
Partials.Message 财务报告和审定财务报表 Partials.Reaction 部件 :
GuildMessageReactionsa 包括: 非特权 网关意图;无 在 Discord 开发者门户网站中需要切换( 不同于MessageContent财务报告和审定财务报表GuildMembers) (中文(简体) ). 要求以盾道接收反应事件.DirectMessageReactions- 也是非特权;必须接受 dMs中的反应事件. Discord.js v14将盾和马克反应视为 单独的意向——如果机器人用DMs操作,两者都需要.Partials.Message+ 键Partials.Reaction—— 仅包括全部 当信息已经存在时, 反应事件有效载荷中的信息对象 在客户端的记忆缓存。 对于在会议之前发出的任何信息 启动(或被从缓存中驱逐),有效载荷作为 部分。 。 。 . 启用这两个部分可以让听众获取全部对象 需求通过reaction.fetch()编号 :reaction.message.fetch(),所以反应 旧消息不会默不作声.
它提供的东西
JSX — 组件 V2 渲染
renderDiscordMessage(ir) 翻译 @copilotkit/channels-ui 词汇到 a
v2 组件有效载荷({ components, flags }与
IS_COMPONENTS_V2 旗帜( n)MessageFlags.IsComponentsV2设置。 其基础是
renderComponents(ir),下层建筑块,它返回一个赤裸的
ContainerBuilder 没有旗帜。 整个消息被包裹在一个单
Container; 子节点地图如下:
| 瓶型元素 | Discord 输出 |
|---|---|
Message | Container (正色 ) setAccentColor(中文(简体) ) |
Header | TextDisplay 与 # 前缀 |
Section 编号 : Markdown | TextDisplay |
Fields | TextDisplay ——每个领域作为 粗体标签 线条 |
Context | TextDisplay ——每个部分作为 -# subtext 线条 |
Actions | 一个或多个 ActionRow编号 |
Button | Button (习惯 id = 薄荷不透明) ck: 页:1 |
Select | StringSelect (习惯 id = 薄荷不透明) ck: 页:1 |
Image | MediaGallery 单项 |
Divider | Separator |
Table | TextDisplay ——有栅栏的代码块通过 discordMarkdown |
经常预算
Discord caps every elements. 破解每个元素。 成像器会随着流出而降解 /
夹子——它从不默默地降低内容. 限制居住 DISCORD_LIMITS数字 :
| 限制 | 数值 | 元素 |
|---|---|---|
componentsPerMessage | 第40条 | 每封信件的(已注销)组件总数 |
actionRows | 页:1 | 每个消息的动作行 |
buttonsPerRow | 页:1 | 每个动作行的按钮 |
selectOptions | 第25条 | 每个字符串选择选项 |
textDisplayChars | 联合国 | 每个文本播放字符 |
buttonLabel | 80个 | 按钮标签字符 |
customId | 100个 | custom_id 字符 |
headerText | 256号 | 信头行字符( e)# 文本播放) |
流线
thread.stream(...) 张贴一个纯文本占位符,并通过
ChunkedMessageStream: 节奏 message.edit 电话间隔~1100毫秒,
2000-Charging, 中流档自动关闭和Markdown →
Discord -floodered Markdown 翻译 所以飞行中的信息总是使.
互动(相对第一)
每个 interactionCreate 事件(按下按钮,选择字符串)立即
承认 deferUpdate (在 • 3个 截止日期,
ackDeadlineMs = 3000) (中文(简体) ). decodeInteraction 然后取出不透明的薄荷
页:1ck:…从 custom_id,并手拿一个 InteractionEvent 页:1
发动机。 信使只携带不透明的ID——没有道具或秘密. 无关
点击解码器可无害地忽略的事件 .
人类的跳动
使用 thread.awaitChoice(<Picker .../>) 发布交互式信息
块以待单击解决;已解决的值是单击控件的
数值。 代理中断( E)on_interrupt)被跑道渲染器所捕获并
发往您的 onInterrupt 处理器,它会张贴一个拾取器;单击
通过 thread.resume(value)。 。 。 .
模块
openModal(view) 针对按钮点击或斜线打开 Discord 模式
命令。 呼叫必须发生 在任何其他答复之前 (Discord's内部)
3秒确认窗口)——先打开模式,再做长跑
在后续信息中开展工作.
Discord 上不支持重新打开验证 . 当用户提交 模式,a
bot.onModalSubmit处理器可能返回{ errors }但Discord已经 没有 API 以每个字段的验证错误重打开相同的模式( 不同于 斯莱克的response_action: "errors"机械师。 这个{ errors }结果是 适配器忽略了该模式deferUpdate随便 调用前验证输入openModal,或张贴后续行动 信件以报告任何提交错误.仅
TextInput字段在 Discord 模式中支持.ModalSelect财务报告和审定财务报表RadioButtons元素在生成时被否决ModalRenderError(哪个)openModal表面为{ ok: false, error }) (中文(简体) ).
电文
Discord ephemeral messages 是交互范围 – Discord 只支持它们
作为按钮点击或斜线命令的初始响应,它们不能是
3秒钟的窗外 为此原因 supportsEphemeral 实值
以 false。 。 。 .
thread.postEphemeral(user, ui, { fallbackToDM: true }) 围绕这个工作
发送信件为 管理部 当本地麻黄是
无. 结果是 { ok: true, usedFallback: true } 所以打电话的人
能够检测到使用DM而不是内道电平.
与 { fallbackToDM: false } 并且没有现场互动, postEphemeral 返回时
null (文献记载为"不倒回哨").
未来加强: 本地互动-社区后续行动(呼吁)
interaction.followUp({ ephemeral: true, … }))是一个 计划增加。 一旦浮出水面,supportsEphemeral将升级为true互动背景和usedFallback将返回false。 。 。 .
打字指标和反应
适配器支持Discord-native两种能力:
- 打字指标 —— 说
channel.sendTyping()在每个开始时调用 运行,给予用户即时反馈. - 反应 —— 说
supportsReactions: true广告; 引擎可以添加 处理过程中的emoji反应.
发件人旗下分辨率
适配器将每个转动的 Discord 用户 ID 解析为 a ProviderActor
(单位:千美元){ id, kind, name?, handle? }),每个ID被缓存. 无法读取 Discord bots
用户电子邮件地址, 因此 ProviderActor.email 保留在这个平台上.
输入文件附件可以下载并交付代理
作为多式内容部分(buildFileContentParts); 工具可以发布文件
通过 thread.postFile(...)。 。 。 .
内建
defaultDiscordTools- 船舶lookup_discord_user这样代理可以解决 名称/处理到<@USERID>提及。 展开到tools。 。 。 .defaultDiscordContext标记程序, Discord Markdown 格式化 引导,以及 Discord 频道/线程对话模式。 展开到context。 。 。 .
工具上下文
工具接收单一共享 ChannelToolContext 从 @copilotkit/channels
(单位:千美元){ thread, message?, user?, signal?, platform })并达到Discord 力量
仅通过能力 thread 方法,此适配器的背面:
thread.getMessages()– 当前频道的最近消息(通过channel.messages.fetch),每个aThreadMessage(单位:千美元){ user?, text, ts?, isBot? }) (中文(简体) ).thread.lookupUser(query)- 解决名称/处理方式ProviderActor以 搜索盾牌成员 .thread.postFile({ bytes, filename, title?, altText? })上传文件 作为附件进入频道.
它保持工具的便携性:用 defineChannelTool({...}) 他们
对任何宣传同样能力的适配器的工作.
斜线命令
当运行时间激活时
频道 (await listener.channels.ready()——通过 registerCommands时
guildId 他们将被立即登记在盾上,没有它,他们
在全球注册,传播需要1小时。 登记处理器
bot.onCommand数字 :
bot.onCommand({
name: "triage",
description: "Summarize the thread and propose issues.",
options: {
// Optional JSON Schema for native Discord slash-command options.
// Generates typed Discord option descriptors via jsonSchemaToDiscordOptions.
},
async handler({ thread, text, user, rawOptions }) {
await thread.runAgent({ prompt: `Triage: ${text}` });
},
});
与Slack不同的是,指令在程序上是注册的——没有显示
文档。 Discord 通过 rawOptions 当
命令的 options 提供了计划;参数也作为免费文本被平了下来
内 ctx.text。 。 。 .
应用程序设置 (授权范围、bot权限、邀请 URL)是通过 Discord开发者门户网站和OAuth2邀请流. 一个完整的线条示例 住在这里
examples/slack- 一个运行的机器人应用程序 黑和/或Discoord 取决于你设置了哪些秘密.
V1里没有什么
- 模块限制 ——支持文本输入模式(最多5个字段).
ModalSelect财务报告和审定财务报表RadioButtons要素在生成时被拒绝. 验证重新打开( E)response_action: "errors"() 不支持 – Discord 没有 API; 提交错误应作为后续消息发布 换句话说. - OAuth / 多盾安装( 只有单一bot 令牌)
- 持久性( Redis/ DB)
ActionStore——只记录;动作在重新启动后到期 - 主动贴出( 机器人只回答旋转它的一部分)
- 自动硬化( 单一)
Client实例) - 原生交互- ephemeral( 相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相相相接相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相
supportsEphemeral实值false; 使用thread.postEphemeral(user, ui, { fallbackToDM: true })基于DM的 工作间)
出口
discord, (中文(简体) ), DiscordAdapter, (中文(简体) ), DiscordAdapterOptions· ; DiscordConversationStore· ;
attachDiscordListener, (中文(简体) ), ListenerConfig, (中文(简体) ), ClientLike, (中文(简体) ), IncomingCommandRaw· ;
createRunRenderer, (中文(简体) ), ChannelLike· ; decodeInteraction· ; conversationKeyOf, (中文(简体) ),
ReplyTarget, (中文(简体) ), IncomingTurn· ; renderComponents, (中文(简体) ), renderDiscordMessage, (中文(简体) ),
DISCORD_LIMITS· ; discordMarkdown· ; MessageStream, (中文(简体) ), MessageStreamConfig· ;
ChunkedMessageStream, (中文(简体) ), ChunkedMessageStreamConfig· ; autoCloseOpenMarkdown· ;
registerCommands, (中文(简体) ), jsonSchemaToDiscordOptions· ; buildFileContentParts, (中文(简体) ),
DiscordAttachmentRef, (中文(简体) ), AgentContentPart, (中文(简体) ), FileDeliveryConfig· ;
defaultDiscordContext, (中文(简体) ), discordTaggingContext, (中文(简体) ), discordFormattingContext, (中文(简体) ),
discordConversationModelContext· ; lookupDiscordUserTool, (中文(简体) ), defaultDiscordTools。 。 。 .