从0开始使用.NET开发QQBot收发群内消息并接入语言大模型
前排提示:博主也是一个c#初学者,如遇有问题的地方欢迎斧正。
本文将要实现的效果如下:

step1.前往QQ机器人申请一个测试用的机器人,填写相关资料,并将测试用的ip地址加入腾讯的白名单。
获取机器人appsecret等信息,并将该机器人加入到你的群聊里面,要求在该群聊里面,你是管理或者群主。
step2. 申请大模型api接口。阅读API调用文档,获取apikey以及url和模型id,供我们以后接入,博主申请的是豆包api接口供测试使用。具体这三个参数是啥csdn也有介绍,这里不再赘述。
step3.使用visual studio 创建一个新的c#解决方案,创建一个c#控制台应用程序,以及用于存放类和方法的库。截止到博主发帖前,.NET原本有的专门为QQ机器人服务的nuget包QQBot4Sharp已经无法使用。故我们采用手搓的方法,安装Newtonsoft.json包用于转换json,安装QQBot4Sharp包用于一个简单的传参。(因为博主原本是用这个包写的,写到最后发现不能使用了,就开始看腾讯api文档开始手搓,前面的也懒得改了)总之基本没有使用这个包。
step4. 先实现将群友的消息接入豆包api接口的方法。
点击查看代码
using Newtonsoft.Json;
using System.Text;
namespace HuHuBot
{
#region 接入豆包ai
public class ChatBot
{
public string ApiKey = "这里填写你的apikey";
public string url = "这里填写你的url,博主的url为https://ark.cn-beijing.volces.com/api/v3/chat/completions";
public string id = "这里填写你的model id,比如博主的id为ep-**********-zzw92";
public static int i = 1;
private static readonly HttpClient client = new HttpClient();
public async Task<string> ChatAi(string mycontent, string username)
{
if (i == 1)
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer "+Apikey);//增加请求头,注意Bearer后面有一个空格
}
Console.WriteLine($"第{i}次回复问题...");
i++;
var requestBody = new
{
model = id,
messages = new[]
{
new{role="system",content="你是小兔Bot。"},//这里填写你想让你的ai扮演的角色。
new{role="user",content="用户询问你如下消息:"+mycontent}
}
};
Console.WriteLine(requestBody);
var RequestJson = JsonConvert.SerializeObject(requestBody);
Console.WriteLine(RequestJson);
var content = new StringContent(RequestJson, Encoding.UTF8, "application/json");
try
{
// client.PostAsync()
var responseMessage = await client.PostAsync(url, content);
responseMessage.EnsureSuccessStatusCode();
string responseBody = await responseMessage.Content.ReadAsStringAsync();
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>(responseBody);
// rootObject.choices
var replyContent = rootObject.choices[0].message.content;
return replyContent;
}
catch (Exception ex)
{
return $"Error! {ex.Message.ToString()}";
}
}
}
public interface TyClient
{
string ChatAi(string input);
}
public abstract class TyBot : TyClient
{
protected string ConfigJson;
public void TyClientBase(string configJson)
{
ConfigJson = configJson;
}
public abstract string ChatAi(string input);
}
public class Message
{
public string content { get; set; }
public string role { get; set; }
}
public class Choice
{
public string finish_reason { get; set; }
public int index { get; set; }
public object logprobs { get; set; }
public Message message { get; set; }
}
public class Usage
{
public int completion_tokens { get; set; }
public int prompt_tokens { get; set; }
public int total_tokens { get; set; }
}
public class RootObject
{
public Choice[] choices { get; set; }
public long created { get; set; }
public string id { get; set; }
public string model { get; set; }
public string @object { get; set; }
public Usage usage { get; set; }
}
#endregion
}
点击查看代码
namespace HuHuBot
{
public class MyRootObject
{
public int op { get; set; }
public int s { get; set; }
public string t { get; set; }
public DObject d { get; set; }
}
public class DObject
{
public int version { get; set; }
public string session_id { get; set; }
public UserObject user { get; set; }
public int[] shard { get; set; }
}
public class UserObject
{
public string id { get; set; }
public string username { get; set; }
public bool bot { get; set; }
public int status { get; set; }
}
public class JsonDataObject
{
public int OperationCode { get; set; }
public int SequenceNumber { get; set; }
public string EventType { get; set; }
public string Identifier { get; set; }
public DataObject Data { get; set; }
}
public class DataObject
{
public AuthorDataObject Author { get; set; }
public string Content { get; set; }
public string GroupId { get; set; }
public string GroupOpenId { get; set; }
public string MessageId { get; set; }
public string Timestamp { get; set; }
}
public class AuthorDataObject
{
public string AuthorId { get; set; }
public string MemberOpenId { get; set; }
public string UnionOpenId { get; set; }
}
public class Author
{
public string id { get; set; }
public string member_openid { get; set; }
public string union_openid { get; set; }
}
public class Data
{
public Author author { get; set; }
public string content { get; set; }
public string group_id { get; set; }
public string group_openid { get; set; }
public string id { get; set; }
public string timestamp { get; set; }
}
public class Root
{
public int op { get; set; }
public int s { get; set; }
public string t { get; set; }
public string id { get; set; }
public Data d { get; set; }
}
}
点击查看代码
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QQBot4Sharp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Reflection.Metadata;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace HuHuBot
{
public class qqAPIGet
{
public static string BotToken;
public static string Msg_id;
public static int Msg_seq=1;
static string receivedMessageStr = "";
static string cuttentMessageStr = "";
#region TokenGET 7200s
public async Task<string> GetToken(BotCreateInfo botCreateInfo)
{
string tokenUrl = "https://bots.qq.com/app/getAppAccessToken";
TokenPostClass tokenPostClass = new TokenPostClass()
{
appId = botCreateInfo.AppID,
clientSecret = botCreateInfo.ClientSecret,
};
{
var str = JsonConvert.SerializeObject(tokenPostClass);
var client = new HttpClient();
HttpContent content = new StringContent(str);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = await client.PostAsync(tokenUrl, content);
// responseMessage.EnsureSuccessStatusCode();
string responseBody = await responseMessage.Content.ReadAsStringAsync();
var tokenReceiveClass = JsonConvert.DeserializeObject<TokenReceiveClass>(responseBody);
BotToken= tokenReceiveClass.access_token;
return tokenReceiveClass.access_token;
}
}
public class TokenPostClass
{
public string appId { get; set; }
public string clientSecret { get; set; }
}
public class TokenReceiveClass
{
public string access_token { get; set; }
public int expires_in { get; set; }
}
#endregion
#region 开放接口请求的权限验证
public static HttpWebRequest HeadAddToken(HttpWebRequest request, string token)
{
string qqToken = "QQBot " + token;
request.Headers.Add("Authorization", qqToken);
return request;
}
#endregion
#region get apiurl
public static async Task<string> HttpWebRequest_Get(string token)
{
Encoding encoding = Encoding.UTF8;
//creat request object.
HttpClient client = new HttpClient();
string url = "https://sandbox.api.sgroup.qq.com/gateway/bot";
string qqToken = "QQBot " + token;
client.DefaultRequestHeaders.Add("Authorization", qqToken);
client.DefaultRequestHeaders.Add("accept", "application/json");
var responseMessage = await client.GetStringAsync(url);
var response = JsonConvert.DeserializeObject<ResponseClass>(responseMessage);
return response.url;
}
public class ResponseClass
{
public string url { get; set; }
public int shards { get; set; }
Session_start_limit session_Start_Limit;
}
public class Session_start_limit
{
public int total { get; set; }
public int remaining { get; set; }
public int reset_after { get; set; }
public int max_concurrency { get; set; }
}
#endregion
#region 长连接建立,并获取数据/登录鉴权,获得session
public async Task ConnectClientWebSocket(string url,string AccessToken)
{
var client = new ClientWebSocket();
try
{
await client.ConnectAsync(new Uri("wss://sandbox.api.sgroup.qq.com/websocket"), CancellationToken.None);
var returnMess=await ReceiveMessages(client);
PayLoadClass payLoadClass = new PayLoadClass();
payLoadClass.op = 2;
payLoadClass.d.token = "QQBot " +AccessToken;
payLoadClass.d.shard = [0, 1];
payLoadClass.d.intents = (1 << 25);
payLoadClass.d.properties = null;
var OpCode2Identify=JsonConvert.SerializeObject(payLoadClass);
await SendMessage(client, OpCode2Identify);
var NewMessageReceived=await ReceiveMessages(client);
MyRootObject root=JsonConvert.DeserializeObject<MyRootObject>(NewMessageReceived);
if (root.t == "READY")
{
_ = ListenForMessages(client);
Console.WriteLine("Sucess Connect");
_ = SendPeriodicMessage(client);
}
// return null;
}
catch (Exception ex) {
Console.WriteLine(ex.Message.ToString());
}
}
static async Task SendMessage(ClientWebSocket client, string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
await client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
static async Task ListenForMessages(ClientWebSocket client)
{
var buffer = new byte[1024];
while (client.State == WebSocketState.Open)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
var receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {receivedMessage}");
receivedMessageStr = receivedMessage;
if(cuttentMessageStr != receivedMessage&&receivedMessage!= "{\"op\":11}")
{
cuttentMessageStr=receivedMessage;
Console.WriteLine($"Receive Message!Message:{cuttentMessageStr}");
ChatBot chatBot = new ChatBot();
//////////////////////////////////////////////
///处理信息
Root ReceivedMess=JsonConvert.DeserializeObject<Root>(cuttentMessageStr);
// msg_id=ReceivedMess
var User=ReceivedMess.d.author.id;
var SendMessage=ReceivedMess.d.content;
Msg_id = ReceivedMess.d.id;
// msg_id=ReceivedMess.d.id;
var HuHuBotMessage=await chatBot.ChatAi(SendMessage, User);
Console.WriteLine(HuHuBotMessage);
if (HuHuBotMessage != null) {
var group_openid = ReceivedMess.d.group_openid;
var SendMessageUrl = "https://sandbox.api.sgroup.qq.com/" + $"v2/groups/{group_openid}/messages";
//记得要加head头.
if (BotToken != null)
{
// await se
await SendMessage2Group(SendMessageUrl, HuHuBotMessage);
}
}
chatBot=null;
}
}
}
}
public class ThisMessageClass
{
public string content { get; set; }
public int msg_type { get; set; }
public string msg_id = Msg_id;
public int msg_seq = Msg_seq;
}
static async Task SendMessage2Group(string SendmessageUrl,string MessageContent)
{
try
{
HttpClient client = new HttpClient();
ThisMessageClass thisMessageClass = new ThisMessageClass();
thisMessageClass.content = MessageContent;
thisMessageClass.msg_type = 0;
var MessageJson = JsonConvert.SerializeObject(thisMessageClass);
HttpContent httpContent = new StringContent(MessageJson);
// httpContent = HeadAddToken(httpContent, BotToken);
client.DefaultRequestHeaders.Add("Authorization", "QQBot "+BotToken);
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
HttpResponseMessage returnMessage = await client.PostAsync(SendmessageUrl, httpContent);
if (returnMessage != null)
{
Console.WriteLine("Success Send Message!");
var a= await returnMessage.Content.ReadAsStringAsync();
Console.WriteLine(a);
}
// client = HeadAddToken(client,BotToken);
}
catch (Exception ex) {
Console.WriteLine(ex.Message.ToString());
}
Msg_seq = Msg_seq + 1;
}
static async Task SendPeriodicMessage(ClientWebSocket client)
{
int i = 1;
while (client.State == WebSocketState.Open)
{
var message = "{\"op\": 1,\"d\": 251}";
if (i == 1) { message = "{\"op\": 1,\"d\":}"; }
else
{
message = "{\"op\": 1,\"d\": 251}";
}
var buffer = Encoding.UTF8.GetBytes(message);
await client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine($"Heatbeat {i} Times");
// 等待一段时间再发送下一次消息,这里设置为 5 秒
i++;
await Task.Delay(5000);
}
}
static async Task<string> ReceiveMessages(ClientWebSocket client)
{
var buffer = new byte[1024];
while (client.State == WebSocketState.Open)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
return null;
}
else
{
var receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {receivedMessage}");
return receivedMessage;
}
}
return null;
}
public class PayLoadClass
{
public int op { get; set; }
public D d= new D();
}
public class D
{
public string token { get; set; }
public int intents { get; set; }
public int[] shard = [0, 1];
public string properties = "{\r\n \"$os\": \"linux\",\r\n \"$browser\": \"my_library\",\r\n \"$device\": \"my_library\"\r\n }";
}
#endregion
}
}
step7.控制台应用
点击查看代码
using HuHuBot;
using Newtonsoft.Json.Linq;
using QQBot4Sharp;
using QQBot4Sharp.Models;
using QQBot4Sharp.Models.QQ;
using Serilog;
using System.Text;
using System.Text.RegularExpressions;
//ChatBot chatbot = new ChatBot();
internal class Program
{
static async Task Main(string[] arg)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.CreateLogger();
// bot
var info = new BotCreateInfo()
{
AppID = "这里填写你的appid",
ClientSecret = "这里填写你的ClientSecret",
Intents = Intents.ALL,
};
qqAPIGet qqAPIGet = new qqAPIGet();
var str=await qqAPIGet.GetToken(info);
var tt = await qqAPIGet.HttpWebRequest_Get(str);
Console.WriteLine(tt);
await qqAPIGet.ConnectClientWebSocket(tt,str);
Console.ReadLine();
}
}

浙公网安备 33010602011771号