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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WeCom.AiBot.Sdk" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WeCom.AiBot.Sdk" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="WeCom.AiBot.Sdk" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WeCom.AiBot.Sdk --version 1.0.6
                    
#r "nuget: WeCom.AiBot.Sdk, 1.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WeCom.AiBot.Sdk@1.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WeCom.AiBot.Sdk&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=WeCom.AiBot.Sdk&version=1.0.6
                    
Install as a Cake Tool

WeCom.AiBot.Sdk

NuGet License .NET

企业微信智能机器人 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.0 tag 后完成发布。

发布需要在仓库 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.WSClientWeCom.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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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