WeCom.AiBot.Sdk
1.0.6
dotnet add package WeCom.AiBot.Sdk --version 1.0.6
NuGet\Install-Package WeCom.AiBot.Sdk -Version 1.0.6
<PackageReference Include="WeCom.AiBot.Sdk" Version="1.0.6" />
<PackageVersion Include="WeCom.AiBot.Sdk" Version="1.0.6" />
<PackageReference Include="WeCom.AiBot.Sdk" />
paket add WeCom.AiBot.Sdk --version 1.0.6
#r "nuget: WeCom.AiBot.Sdk, 1.0.6"
#:package WeCom.AiBot.Sdk@1.0.6
#addin nuget:?package=WeCom.AiBot.Sdk&version=1.0.6
#tool nuget:?package=WeCom.AiBot.Sdk&version=1.0.6
WeCom.AiBot.Sdk
企业微信智能机器人 C# SDK —— 基于 WebSocket 长连接通道,提供消息收发、流式回复、模板卡片、事件回调、文件下载解密、媒体素材上传等核心能力。与 Node.js 版 SDK 功能完全一致。
✨ 特性
- 🔗 WebSocket 长连接 — 基于
wss://openws.work.weixin.qq.com内置默认地址,开箱即用【注:私有部署企业需要在企业管理端查看该长连接地址】 - 🔐 自动认证 — 连接建立后自动发送认证帧(botId + secret)
- 💓 心跳保活 — 自动维护心跳,连续未收到 ack 时自动判定连接异常
- 🔄 断线重连 — 指数退避重连策略(1s → 2s → 4s → ... → 30s 上限),支持自定义最大重连次数
- 📨 消息分发 — 自动解析消息类型并触发对应 .NET 事件(text / image / mixed / voice / file / video)
- 🌊 流式回复 — 内置流式回复方法,支持 Markdown 和图文混排
- 🃏 模板卡片 — 支持回复模板卡片消息、流式+卡片组合回复、更新卡片
- 📤 主动推送 — 支持向指定会话主动发送 Markdown、模板卡片或媒体消息,无需依赖回调帧
- 📡 事件回调 — 支持进入会话、模板卡片按钮点击、用户反馈等事件
- ⏩ 串行回复队列 — 同一 req_id 的回复消息串行发送,自动等待回执
- 🔒 文件下载解密 — 内置 AES-256-CBC 文件解密,每个图片/文件消息自带独立的 aeskey
- 📎 媒体素材上传 — 支持分片上传临时素材(file/image/voice/video),自动管理并发与重试
- 🪵 可插拔日志 — 基于
Microsoft.Extensions.Logging.ILogger,支持注入任意兼容工厂;未注入时使用自带 ConsoleLogger - 🧩 Webhook 加解密 — 提供
WecomCrypto类,支持回调 URL 的消息加解密与签名校验
📦 安装
方式一:NuGet(推荐)
包地址:https://www.nuget.org/packages/WeCom.AiBot.Sdk
dotnet add package WeCom.AiBot.Sdk
也可通过 Visual Studio 的 NuGet 包管理器搜索 WeCom.AiBot.Sdk 进行安装,或使用 Package Manager Console:
Install-Package WeCom.AiBot.Sdk
或通过 PackageReference 在 .csproj 中手动声明(请使用 NuGet 上的最新版本号替换 x.y.z):
<PackageReference Include="WeCom.AiBot.Sdk" Version="x.y.z" />
方式二:项目引用
# 克隆仓库后,在你的项目目录下执行
dotnet add reference ../aibot-csharp-sdk/src/WeCom.AiBot.Sdk/WeCom.AiBot.Sdk.csproj
环境要求
- .NET 8.0(LTS)或更高版本
- 外部依赖仅
Microsoft.Extensions.Logging.Abstractions
版本与发布
SDK 通过 GitHub Actions 自动化发布到 NuGet:
- Tag 触发:推送形如
v1.0.0的 tag,会自动执行 build → test → pack → push 到 NuGet,并创建对应的 GitHub Release。 - 手动触发:在仓库的 Actions → Release C# SDK 工作流点击
Run workflow,输入版本号(如1.0.0),工作流会自动创建并推送v1.0.0tag 后完成发布。
发布需要在仓库 Secrets 中配置 NUGET_API_KEY(在 nuget.org 创建)。
🚀 快速开始
using WeCom.AiBot.Sdk;
using WeCom.AiBot.Sdk.Models;
using WeCom.AiBot.Sdk.Utils;
// 1. 创建客户端实例
var wsClient = new WSClient(new WSClientOptions
{
BotId = "your-bot-id", // 企业微信后台获取的机器人 ID
Secret = "your-bot-secret", // 企业微信后台获取的机器人 Secret
});
// 2. 监听认证成功
wsClient.Authenticated += (_, _) =>
{
Console.WriteLine("🔐 认证成功");
};
// 3. 监听文本消息并进行流式回复
wsClient.MessageTextReceived += async (_, e) =>
{
var content = e.Frame.Body?.GetProperty("text").GetProperty("content").GetString();
Console.WriteLine($"收到文本: {content}");
var streamId = ReqIdGenerator.GenerateReqId("stream");
// 发送流式中间内容
await wsClient.ReplyStreamAsync(e.Frame, streamId, "正在思考中...", false);
// 发送最终结果
await Task.Delay(1000);
await wsClient.ReplyStreamAsync(e.Frame, streamId, $"你好!你说的是: \"{content}\"", true);
};
// 4. 监听进入会话事件(发送欢迎语)
wsClient.EventEnterChat += async (_, e) =>
{
await wsClient.ReplyWelcomeAsync(e.Frame, new WelcomeTextReplyBody
{
Text = new WelcomeTextReplyBody.TextData { Content = "您好!我是智能助手,有什么可以帮您的吗?" }
});
};
// 5. 建立连接
wsClient.Connect();
// 6. 优雅退出
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
wsClient.Disconnect();
};
Console.WriteLine("按 Ctrl+C 退出...");
Thread.Sleep(Timeout.Infinite);
📖 API 文档
WSClient
核心客户端类,提供连接管理、消息收发等功能。
构造函数
var wsClient = new WSClient(options);
方法一览
| 方法 | 说明 | 返回值 |
|---|---|---|
Connect() |
建立 WebSocket 连接,连接后自动认证 | void |
Disconnect() |
主动断开连接 | void |
ReplyAsync(frame, body, cmd?) |
通过 WebSocket 通道发送回复消息(通用方法) | Task<WsFrame> |
ReplyStreamAsync(frame, streamId, content, finish?, msgItem?, feedback?) |
发送流式文本回复(支持 Markdown) | Task<WsFrame> |
ReplyWelcomeAsync(frame, body) |
发送欢迎语回复(文本或模板卡片),需 5s 内调用 | Task<WsFrame> |
ReplyTemplateCardAsync(frame, templateCard, feedback?) |
回复模板卡片消息 | Task<WsFrame> |
ReplyStreamWithCardAsync(frame, streamId, content, finish?, msgItem?, streamFeedback?, templateCard?, cardFeedback?) |
流式消息 + 模板卡片组合回复 | Task<WsFrame> |
UpdateTemplateCardAsync(frame, templateCard, userIds?) |
更新模板卡片(响应 template_card_event),需 5s 内调用 | Task<WsFrame> |
SendMessageAsync(chatid, body) |
主动发送消息(Markdown / 模板卡片 / 媒体),无需回调帧 | Task<WsFrame> |
UploadMediaAsync(fileBuffer, options, cancellationToken?) |
上传临时素材(三步分片上传),返回 media_id |
Task<UploadMediaFinishResult> |
ReplyMediaAsync(frame, mediaType, mediaId, videoOptions?) |
被动回复媒体消息(file/image/voice/video) | Task<WsFrame> |
SendMediaMessageAsync(chatid, mediaType, mediaId, videoOptions?) |
主动发送媒体消息 | Task<WsFrame> |
DownloadFileAsync(url, aesKey?) |
下载文件并 AES 解密,返回字节数组及文件名 | Task<(byte[] Buffer, string? Filename)> |
HasPendingReplyAck(frame) |
检查指定帧是否有未完成的 ack | bool |
ReplyStreamNonBlockingAsync(frame, streamId, content, finish?, msgItem?, feedback?) |
非阻塞流式回复(有 pending ack 时跳过,返回 null) |
Task<WsFrame?> |
属性
| 属性 | 说明 | 类型 |
|---|---|---|
IsConnected |
当前 WebSocket 连接状态 | bool |
Api |
内部 API 客户端实例(高级用途,如文件下载) | WeComApiClient |
ReplyStreamAsync 详细说明
发送流式文本回复(便捷方法,支持 Markdown)。
await wsClient.ReplyStreamAsync(
frame: WsFrame, // 收到的原始 WebSocket 帧(透传 req_id)
streamId: "stream_xxx", // 流式消息 ID(使用 ReqIdGenerator.GenerateReqId("stream") 生成)
content: "回复内容", // 回复内容(支持 Markdown),最长 20480 字节
finish: false, // 是否结束流式消息,默认 false
msgItem: null, // 图文混排项(仅 finish=true 时有效,最多 10 个)
feedback: null // 反馈信息(仅首次回复时设置)
);
使用示例:
var streamId = ReqIdGenerator.GenerateReqId("stream");
// 发送流式中间内容
await wsClient.ReplyStreamAsync(e.Frame, streamId, "正在处理中...", false);
// 发送最终结果(finish=true 表示结束流)
await wsClient.ReplyStreamAsync(e.Frame, streamId, "处理完成!结果是...", true);
ReplyWelcomeAsync 详细说明
发送欢迎语回复,需在收到 EventEnterChat 事件 5 秒内调用,超时将无法发送。
// 文本欢迎语
await wsClient.ReplyWelcomeAsync(e.Frame, new WelcomeTextReplyBody
{
Text = new WelcomeTextReplyBody.TextData { Content = "欢迎!" }
});
// 模板卡片欢迎语
await wsClient.ReplyWelcomeAsync(e.Frame, new WelcomeTemplateCardReplyBody
{
TemplateCard = new TemplateCard { CardType = "text_notice", MainTitle = new() { Title = "欢迎" } }
});
ReplyTemplateCardAsync 详细说明
回复模板卡片消息。收到消息回调或进入会话事件后使用。
await wsClient.ReplyTemplateCardAsync(
frame: e.Frame, // 收到的原始 WebSocket 帧
templateCard: new TemplateCard { /* ... */ }, // 模板卡片内容
feedback: null // 反馈信息(可选)
);
ReplyStreamWithCardAsync 详细说明
发送流式消息 + 模板卡片组合回复。首次回复时必须返回 stream 的 id;template_card 同一消息只能回复一次。
await wsClient.ReplyStreamWithCardAsync(
frame: e.Frame,
streamId: streamId,
content: "回复内容",
finish: false,
msgItem: null, // 图文混排项(仅 finish=true 时有效)
streamFeedback: null, // 流式消息反馈信息(首次回复时设置)
templateCard: templateCard, // 模板卡片内容(同一消息只能回复一次)
cardFeedback: null // 模板卡片反馈信息
);
使用示例:
var streamId = ReqIdGenerator.GenerateReqId("stream");
// 首次回复:带卡片
await wsClient.ReplyStreamWithCardAsync(e.Frame, streamId, "正在处理...", false,
templateCard: new TemplateCard
{
CardType = "button_interaction",
MainTitle = new() { Title = "操作面板" },
ButtonList = new List<TemplateCardButton>
{
new() { Text = "确认", Key = "confirm" }
},
TaskId = $"task_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"
});
// 流式结束
await wsClient.ReplyStreamWithCardAsync(e.Frame, streamId, "处理完成!", true);
UpdateTemplateCardAsync 详细说明
更新模板卡片,需在收到 EventTemplateCardEvent 事件 5 秒内调用。
await wsClient.UpdateTemplateCardAsync(
frame: e.Frame, // 对应事件的 WebSocket 帧
templateCard: new TemplateCard { /* task_id 需与回调一致 */ },
userIds: null // 要替换消息的 userid 列表,不填则替换所有用户
);
SendMessageAsync 详细说明
主动向指定会话推送消息,无需依赖收到的回调帧。
await wsClient.SendMessageAsync(
chatid: "userid_or_chatid", // 单聊填 userid,群聊填 chatid
body: new SendMarkdownMsgBody
{
Markdown = new SendMarkdownMsgBody.MarkdownData { Content = "这是一条**主动推送**的消息" }
}
);
UploadMediaAsync 详细说明
通过 WebSocket 长连接执行三步分片上传:init → chunk × N → finish。
- 单个分片不超过 512KB(Base64 编码前),最多 100 个分片(约 50MB 上限)
- 自动根据分片数调整并发数(1~4 分片全并发;5~10 分片并发 3;>10 分片并发 2)
- 单分片上传失败自动重试(最多 2 次)
var result = await wsClient.UploadMediaAsync(
fileBuffer: File.ReadAllBytes("/path/to/image.png"),
options: new UploadMediaOptions { Type = WeComMediaType.Image, Filename = "image.png" }
);
Console.WriteLine($"上传成功,media_id: {result.MediaId}");
ReplyMediaAsync / SendMediaMessageAsync 详细说明
被动回复 / 主动发送媒体消息。mediaType 取值为 WeComMediaType.File / Image / Voice / Video。
// 被动回复图片
await wsClient.ReplyMediaAsync(e.Frame, WeComMediaType.Image, result.MediaId);
// 主动发送视频(可附标题和描述)
await wsClient.SendMediaMessageAsync("userid", WeComMediaType.Video, result.MediaId,
new VideoOptions { Title = "视频标题", Description = "视频描述" });
DownloadFileAsync 使用示例
wsClient.MessageImageReceived += async (_, e) =>
{
var imageUrl = e.Frame.Body?.GetProperty("image").GetProperty("url").GetString();
var aesKey = e.Frame.Body?.GetProperty("image").GetProperty("aeskey").GetString();
if (imageUrl == null) return;
var (buffer, filename) = await wsClient.DownloadFileAsync(imageUrl, aesKey);
Console.WriteLine($"文件名: {filename}, 大小: {buffer.Length} bytes");
await File.WriteAllBytesAsync(filename ?? "image.jpg", buffer);
};
⚙️ 配置选项
WSClientOptions 完整配置:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
BotId |
string |
✅ | — | 机器人 ID(企业微信后台获取) |
Secret |
string |
✅ | — | 机器人 Secret(企业微信后台获取) |
Scene |
int? |
— | null |
场景值(可选) |
PlugVersion |
string? |
— | null |
插件版本(可选) |
ReconnectInterval |
int |
— | 1000 |
重连基础延迟(毫秒),实际延迟按指数退避递增 |
MaxReconnectAttempts |
int |
— | 10 |
最大重连次数(-1 表示无限重连) |
MaxAuthFailureAttempts |
int |
— | 5 |
最大认证失败重试次数(-1 表示无限重连) |
HeartbeatInterval |
int |
— | 30000 |
心跳间隔(毫秒) |
RequestTimeout |
int |
— | 10000 |
HTTP 请求超时时间(毫秒) |
WsUrl |
string? |
— | wss://openws.work.weixin.qq.com |
自定义 WebSocket 连接地址 |
WsOptions |
WsClientOptions? |
— | null |
TLS 证书配置(自签证书等) |
MaxReplyQueueSize |
int |
— | 500 |
单个 req_id 的回复队列最大长度 |
LoggerFactory |
ILoggerFactory? |
— | null(使用内置 ConsoleLogger) |
自定义日志工厂 |
📡 事件列表
所有事件均为标准 .NET event EventHandler<T> 模式:
| 事件 | 参数类型 | 说明 |
|---|---|---|
Connected |
EventArgs |
WebSocket 连接建立 |
Authenticated |
EventArgs |
认证成功 |
Disconnected |
DisconnectedEventArgs (.Reason) |
连接断开 |
Reconnecting |
ReconnectingEventArgs (.Attempt) |
正在重连(第 N 次) |
Error |
ErrorEventArgs (.Exception) |
发生错误 |
MessageReceived |
WsFrameEventArgs (.Frame) |
收到消息(所有类型) |
MessageTextReceived |
WsFrameEventArgs |
收到文本消息 |
MessageImageReceived |
WsFrameEventArgs |
收到图片消息 |
MessageMixedReceived |
WsFrameEventArgs |
收到图文混排消息 |
MessageVoiceReceived |
WsFrameEventArgs |
收到语音消息 |
MessageFileReceived |
WsFrameEventArgs |
收到文件消息 |
MessageVideoReceived |
WsFrameEventArgs |
收到视频消息 |
EventReceived |
WsFrameEventArgs |
收到事件回调(所有事件类型) |
EventEnterChat |
WsFrameEventArgs |
用户进入会话事件 |
EventTemplateCardEvent |
WsFrameEventArgs |
模板卡片按钮点击事件 |
EventFeedbackEvent |
WsFrameEventArgs |
用户反馈事件 |
EventDisconnectedEvent |
WsFrameEventArgs |
服务端主动断开事件(新连接建立) |
EventUnknown |
UnknownEventTypeEventArgs (.Frame, .EventType) |
收到未知类型的事件回调(协议新增 eventtype 时可观察) |
📋 消息类型
SDK 支持以下消息类型(MessageType 枚举):
| 类型 | 值 | 说明 |
|---|---|---|
Text |
text |
文本消息 |
Image |
image |
图片消息(URL 已加密,使用消息中的 image.aeskey 解密) |
Mixed |
mixed |
图文混排消息(包含 text / image 子项) |
Voice |
voice |
语音消息(已转文本) |
File |
file |
文件消息(URL 已加密,使用消息中的 file.aeskey 解密) |
Video |
video |
视频消息 |
SDK 支持以下事件类型(EventType 枚举):
| 类型 | 值 | 说明 |
|---|---|---|
EnterChat |
enter_chat |
进入会话事件 |
TemplateCardEvent |
template_card_event |
模板卡片按钮点击事件 |
FeedbackEvent |
feedback_event |
用户反馈事件 |
DisconnectedEvent |
disconnected_event |
服务端主动断开事件 |
🃏 模板卡片类型
| 类型 | 值 | 说明 |
|---|---|---|
TextNotice |
text_notice |
文本通知模版卡片 |
NewsNotice |
news_notice |
图文展示模版卡片 |
ButtonInteraction |
button_interaction |
按钮交互模版卡片 |
VoteInteraction |
vote_interaction |
投票选择模版卡片 |
MultipleInteraction |
multiple_interaction |
多项选择模版卡片 |
🔀 消息帧结构
WsFrame
public class WsFrame
{
public string? Cmd { get; set; } // 命令类型
public WsFrameHeaders Headers { get; set; } // 请求头(含 req_id)
public JsonElement? Body { get; set; } // 消息体(任意 JSON 结构)
public int? Errcode { get; set; } // 响应错误码
public string? Errmsg { get; set; } // 响应错误信息
}
WsFrameHeaders
public class WsFrameHeaders
{
public string ReqId { get; set; } // 请求 ID(回复时需透传)
[JsonExtensionData]
public IDictionary<string, JsonElement>? ExtensionData { get; set; }
}
🪵 自定义日志
SDK 使用 Microsoft.Extensions.Logging.ILogger 作为统一日志接口。通过 WSClientOptions.LoggerFactory 注入任意兼容工厂:
// 使用 Serilog
using Serilog;
var loggerFactory = LoggerFactory.Create(builder =>
builder.AddSerilog(Log.Logger));
var wsClient = new WSClient(new WSClientOptions
{
BotId = "your-bot-id",
Secret = "your-bot-secret",
LoggerFactory = loggerFactory,
});
未注入 LoggerFactory 时,SDK 使用内置的 ConsoleLoggerProvider,输出格式为 [ISO8601] [AiBotSDK] [LEVEL] message。如需完全静默日志,可传入 NullLoggerFactory.Instance。
各组件按 ILogger<T> 分类,分类名以 WeCom.AiBot.Sdk. 为前缀(如 WeCom.AiBot.Sdk.WSClient、WeCom.AiBot.Sdk.Ws.WsConnectionManager)。
🔒 Webhook 加解密(WecomCrypto)
WecomCrypto 类用于企业微信回调 URL 的消息加解密与签名校验,对齐 Node 版 wecom-crypto 模块。
using WeCom.AiBot.Sdk.Crypto;
var crypto = new WecomCrypto(token: "your-token", encodingAESKey: "your-encoding-aes-key", receiveId: "your-corpid");
// 加密
var (encrypt, signature) = crypto.Encrypt("plaintext message", timestamp, nonce);
// 验签
if (crypto.VerifySignature(signature, timestamp, nonce, encrypt))
{
// 解密
var decrypted = crypto.Decrypt(encrypt);
}
PKCS#7 填充块大小固定为 32(企业微信规范,非 AES 默认的 16)。ComputeSignature 返回 40 字符小写十六进制 SHA1 签名。
🔧 WebSocket 命令协议
以下为 SDK 内部使用的 WebSocket 命令常量(WsCmd),了解底层协议有助于高级调试:
| 方向 | 常量 | 值 | 说明 |
|---|---|---|---|
| 开发者 → 企微 | Subscribe |
aibot_subscribe |
认证订阅 |
| 开发者 → 企微 | Heartbeat |
ping |
心跳 |
| 开发者 → 企微 | Response |
aibot_respond_msg |
回复消息 |
| 开发者 → 企微 | ResponseWelcome |
aibot_respond_welcome_msg |
回复欢迎语 |
| 开发者 → 企微 | ResponseUpdate |
aibot_respond_update_msg |
更新模板卡片 |
| 开发者 → 企微 | SendMsg |
aibot_send_msg |
主动发送消息 |
| 开发者 → 企微 | UploadMediaInit |
aibot_upload_media_init |
上传素材 - 初始化 |
| 开发者 → 企微 | UploadMediaChunk |
aibot_upload_media_chunk |
上传素材 - 分片 |
| 开发者 → 企微 | UploadMediaFinish |
aibot_upload_media_finish |
上传素材 - 完成 |
| 企微 → 开发者 | Callback |
aibot_msg_callback |
消息推送回调 |
| 企微 → 开发者 | EventCallback |
aibot_event_callback |
事件推送回调 |
📂 项目结构
aibot-csharp-sdk/
├── src/
│ └── WeCom.AiBot.Sdk/
│ ├── WSClient.cs # 核心客户端
│ ├── WeComApiClient.cs # HTTP API 客户端(文件下载)
│ ├── MessageHandler.cs # 消息解析与事件分发
│ ├── Ws/
│ │ └── WsConnectionManager.cs # WebSocket 长连接管理器
│ ├── Crypto/
│ │ ├── FileDecryptor.cs # AES-256-CBC 文件解密
│ │ └── WecomCrypto.cs # Webhook 加解密
│ ├── Models/ # 协议与消息类型
│ ├── Events/ # 事件参数类型
│ ├── Errors/ # 异常类型
│ ├── Logger/ # 日志(ConsoleLoggerProvider 等)
│ └── Utils/ # 工具方法(ReqIdGenerator 等)
├── tests/
│ └── WeCom.AiBot.Sdk.Tests/ # xUnit 单元测试
├── examples/
│ └── Basic/ # 基础使用示例
└── aibot-csharp-sdk.sln
🧩 完整使用示例
流式回复 + 图文混排
wsClient.MessageTextReceived += async (_, e) =>
{
var streamId = ReqIdGenerator.GenerateReqId("stream");
// 流式中间内容
await wsClient.ReplyStreamAsync(e.Frame, streamId, "正在生成图文内容...", false);
// 准备图文混排项(仅 finish=true 时有效)
var imageData = await File.ReadAllBytesAsync("/path/to/image.jpg");
var base64 = Convert.ToBase64String(imageData);
var md5 = Convert.ToHexString(System.Security.Cryptography.MD5.HashData(imageData)).ToLowerInvariant();
var msgItem = new List<ReplyMsgItem>
{
new() { Image = new ReplyMsgItem.ImageData { Base64 = base64, Md5 = md5 } }
};
// 流式结束,附带图片
await wsClient.ReplyStreamAsync(e.Frame, streamId, "这是最终结果", true, msgItem);
};
上传素材 + 回复媒体消息
wsClient.MessageTextReceived += async (_, e) =>
{
var fileBuffer = await File.ReadAllBytesAsync("/path/to/document.pdf");
var result = await wsClient.UploadMediaAsync(fileBuffer, new UploadMediaOptions
{
Type = WeComMediaType.File,
Filename = "document.pdf"
});
// 使用 media_id 被动回复文件消息
await wsClient.ReplyMediaAsync(e.Frame, WeComMediaType.File, result.MediaId);
};
主动推送消息
wsClient.Authenticated += async (_, _) =>
{
// 向指定用户推送 Markdown 消息
await wsClient.SendMessageAsync("target_userid", new SendMarkdownMsgBody
{
Markdown = new SendMarkdownMsgBody.MarkdownData { Content = "# 通知\n\n这是一条**主动推送**的消息。" }
});
// 主动推送媒体消息
var imageBuffer = await File.ReadAllBytesAsync("/path/to/photo.jpg");
var result = await wsClient.UploadMediaAsync(imageBuffer, new UploadMediaOptions
{
Type = WeComMediaType.Image,
Filename = "photo.jpg"
});
await wsClient.SendMediaMessageAsync("target_userid", WeComMediaType.Image, result.MediaId);
};
模板卡片交互
// 回复带按钮的模板卡片
wsClient.MessageTextReceived += async (_, e) =>
{
await wsClient.ReplyTemplateCardAsync(e.Frame, new TemplateCard
{
CardType = "button_interaction",
MainTitle = new() { Title = "请选择操作", Desc = "点击下方按钮进行操作" },
ButtonList = new List<TemplateCardButton>
{
new() { Text = "确认", Key = "btn_confirm", Style = 1 },
new() { Text = "取消", Key = "btn_cancel", Style = 2 }
},
TaskId = $"task_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"
});
};
// 监听卡片按钮点击事件并更新卡片
wsClient.EventTemplateCardEvent += async (_, e) =>
{
var eventKey = e.Frame.Body?.GetProperty("event").GetProperty("event_key").GetString();
var taskId = e.Frame.Body?.GetProperty("event").GetProperty("task_id").GetString();
await wsClient.UpdateTemplateCardAsync(e.Frame, new TemplateCard
{
CardType = "text_notice",
MainTitle = new() { Title = eventKey == "btn_confirm" ? "已确认 ✅" : "已取消 ❌" },
TaskId = taskId!
});
};
❗ 异常类型
| 异常 | Code | 说明 |
|---|---|---|
WSAuthFailureException |
WS_AUTH_FAILURE_EXHAUSTED |
认证失败次数用尽(通常为 botId/secret 配置错误) |
WSReconnectExhaustedException |
WS_RECONNECT_EXHAUSTED |
重连次数用尽(网络或服务端持续不可用) |
WsAckException |
— | 回复 ack 帧返回非 0 errcode(.Frame 属性携带原始帧,供程序化重试或降级决策) |
🔧 开发
# 构建解决方案
dotnet build aibot-csharp-sdk.sln
# 运行测试
dotnet test
# 运行示例(需先替换 BotId / Secret)
dotnet run --project examples/Basic
📄 License
Apache License 2.0,详见 LICENSE.txt。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.6 | 112 | 7/14/2026 |