private async static System.Threading.Tasks.Task <bool> Session_DisconnectedEvt(IMiraiHttpSession sender, Exception e) { logger("Connection", e.Message); while (true) { try { logger("Connection", "Reconnecting..."); MiraiHttpSessionOptions options = new MiraiHttpSessionOptions(host, port, key); await sender.ConnectAsync(me_qq); if (!e.Message.Contains("未知的消息类型")) { MainHolder.broadcaster.BroadcastToAdminGroup("[断线重连]\n诊断报告:" + e.Message + "\n" + e.StackTrace); } break; } catch (Exception err) { logger("Connection", err.Message); await Task.Delay(1000); } } return(false); }
private async Task <IMiraiHttpSession> InternalRetriveSessionAsync(long robotQQ, CancellationToken token) { try { if (_robotScopes.TryGetValue(robotQQ, out IServiceScope? dualcheck)) { return(dualcheck.ServiceProvider.GetRequiredService <IMiraiHttpSession>()); } IServiceScope scope = _services.CreateScope(); try { IMiraiHttpSession session = scope.ServiceProvider.GetRequiredService <IMiraiHttpSession>(); await session.ConnectAsync(robotQQ, token).ConfigureAwait(false); _robotScopes[robotQQ] = scope; return(session); } catch { scope.Dispose(); throw; } } finally { _robotInitTasks.TryRemove(robotQQ, out _); } }
public static async Task Main() { // 一套能用的框架, 必须要注册至少一个 Invoker, Parser, Client 和 Handler // Invoker 负责消息调度 // Parser 负责解析消息到具体接口以便调度器调用相关 Handler 下的处理方法 // Client 负责收发原始数据 IServiceProvider services = new ServiceCollection().AddMiraiBaseFramework() // 表示使用基于基础框架的构建器 .AddHandler <MiraiPlugin>() // 虽然可以把 HttpApiPlugin 作为泛型参数塞入, 但不建议这么做 .Services .AddDefaultMiraiHttpFramework() // 表示使用 mirai-api-http 实现的构建器 .ResolveParser <DynamicPlugin>() // 只提前解析 DynamicPlugin 将要用到的消息解析器 .AddInvoker <MiraiHttpMessageHandlerInvoker>() // 使用默认的调度器 .AddHandler <HttpApiPlugin>() // 可以如此添加更多消息处理器 .AddClient <MiraiHttpSession>() // 使用默认的客户端 .Services // 由于 MiraiHttpSession 使用 IOptions<MiraiHttpSessionOptions>, 其作为 Singleton 被注册 // 配置此项将配置基于此 IServiceProvider 全局的连接配置 // 如果你想一个作用域一个配置的话 // 自行做一个实现类, 继承MiraiHttpSession, 构造参数中使用 IOptionsSnapshot<MiraiHttpSessionOptions> // 并将其传递给父类的构造参数 // 然后在每一个作用域中!先!配置好 IOptionsSnapshot<MiraiHttpSessionOptions>, 再尝试获取 IMiraiHttpSession .Configure <MiraiHttpSessionOptions>(options => { options.Host = "域名/IP"; options.Port = 12345; // 端口 options.AuthKey = "00000000000000000000000000000000"; // 凭据 }) .AddLogging() .BuildServiceProvider(); IServiceScope scope = services.CreateScope(); await using var x = (IAsyncDisposable)scope; //await using AsyncServiceScope scope = services.CreateAsyncScope(); // 自 .NET 6.0 起才可以如此操作代替上边两句 services = scope.ServiceProvider; IMiraiHttpSession session = services.GetRequiredService <IMiraiHttpSession>(); // 大部分服务都基于接口注册, 请使用接口作为类型解析 DynamicPlugin plugin = new DynamicPlugin(); PluginResistration resistration = session.AddPlugin(plugin); // 实时添加 await session.ConnectAsync(0); // 填入期望连接到的机器人QQ号 while (true) { if (Console.ReadLine() == "exit") { resistration.Dispose(); // 实时移除 break; } } }
public async Task HandleMessageAsync(IMiraiHttpSession session, IDisconnectedEventArgs e) { // e.Exception: 引发掉线的响应异常, 按需处理 while (true) { try { await session.ConnectAsync(e.LastConnectedQQNumber); e.BlockRemainingHandlers = true; break; } catch (ObjectDisposedException) // session 已被释放 { break; } catch (Exception) { await Task.Delay(1000); } } }
public async Task HandleMessageAsync(IMiraiHttpSession session, IDisconnectedEventArgs message) { _logger.LogWarning(message.Exception, $"连接被断开。上一次连接的QQ号为:{message.LastConnectedQQNumber}"); while (!session.Connected) // 防止以前的handler未能正确阻断消息传递 { try { await session.ConnectAsync(message.LastConnectedQQNumber); message.BlockRemainingHandlers = true; _logger.LogInformation($"重连成功"); break; } catch (ObjectDisposedException) { break; } catch (Exception e) { _logger.LogError(e, $"重连失败。上一次连接的QQ号为:{message.LastConnectedQQNumber}"); await Task.Delay(1000); } } }
static async Task Main(string[] args) { Options opt = null; Parser.Default.ParseArguments <Options>(args).WithParsed <Options>(o => { opt = o; }); var bot = new Bot(); IServiceProvider services = new ServiceCollection().AddMiraiBaseFramework() // 表示使用基于基础框架的构建器 .AddHandler <Bot>(p => bot, ServiceLifetime.Singleton) .Services .AddDefaultMiraiHttpFramework() // 表示使用 mirai-api-http 实现的构建器 .ResolveParser <Bot>(ServiceLifetime.Singleton) .AddInvoker <MiraiHttpMessageHandlerInvoker>() // 使用默认的调度器 .AddClient <MiraiHttpSession>() // 使用默认的客户端 .Services // 由于 MiraiHttpSession 使用 IOptions<MiraiHttpSessionOptions>, 其作为 Singleton 被注册 // 配置此项将配置基于此 IServiceProvider 全局的连接配置 // 如果你想一个作用域一个配置的话 // 自行做一个实现类, 继承MiraiHttpSession, 构造参数中使用 IOptionsSnapshot<MiraiHttpSessionOptions> // 并将其传递给父类的构造参数 // 然后在每一个作用域中!先!配置好 IOptionsSnapshot<MiraiHttpSessionOptions>, 再尝试获取 IMiraiHttpSession .Configure <MiraiHttpSessionOptions>(options => { options.Host = "127.0.0.1"; options.Port = 8080; // 端口 options.AuthKey = opt.AuthKey; // 凭据 }) .AddLogging() .BuildServiceProvider(); IServiceScope scope = services.CreateScope(); await using var x = (IAsyncDisposable)scope; //await using AsyncServiceScope scope = services.CreateAsyncScope(); // 自 .NET 6.0 起才可以如此操作代替上边两句 services = scope.ServiceProvider; IMiraiHttpSession session = services.GetRequiredService <IMiraiHttpSession>(); // 大部分服务都基于接口注册, 请使用接口作为类型解析 //session.AddPlugin(bot); // 实时添加 bot.SetSession(session); await session.ConnectAsync(opt.QQ); // 填入期望连接到的机器人QQ号 await bot.Start(); while (true) { var readline = await Console.In.ReadLineAsync(); if (readline == "exit") { return; } else if (readline.StartsWith("notice ", StringComparison.OrdinalIgnoreCase)) { var message = $"[通知]{readline.Substring(7)}"; var groups = GensouSakuya.QQBot.Core.QQManager.GroupMemberManager.GroupMembers.Values .Select(p => p.GroupId).Distinct().ToList(); groups.ForEach(p => { session.SendGroupMessageAsync(p, new PlainMessage(message)); }); } else if (readline.StartsWith("togroup ", StringComparison.OrdinalIgnoreCase)) { var splited = readline.Split(" "); if (!long.TryParse(splited[1], out var groupNo)) { continue; } var message = string.Join(" ", splited.Skip(2)); await session.SendGroupMessageAsync(groupNo, new PlainMessage(message)); } else if (readline.Equals("save", StringComparison.OrdinalIgnoreCase)) { await DataManager.Save(); } else if (readline.Equals("load", StringComparison.OrdinalIgnoreCase)) { await DataManager.Load(); } } }
public async Task HandleMessageAsync(IMiraiHttpSession client, IDisconnectedEventArgs message) { _logger.Error("disconnected, reconnecting"); await client.ConnectAsync(client.QQNumber.Value); }
public static async Task Connect(long qqId, string ip, ushort port, string authKey, Action <bool, string> ConnectedEvent) { try { // 一套能用的框架, 必须要注册至少一个 Invoker, Parser, Client 和 Handler // Invoker 负责消息调度 // Parser 负责解析消息到具体接口以便调度器调用相关 Handler 下的处理方法 // Client 负责收发原始数据 IServiceProvider services = new ServiceCollection().AddMiraiBaseFramework() // 表示使用基于基础框架的构建器 .Services .AddDefaultMiraiHttpFramework() // 表示使用 mirai-api-http 实现的构建器 .AddInvoker <MiraiHttpMessageHandlerInvoker>() // 使用默认的调度器 .AddHandler <GroupMessage>() // 群消息 .AddHandler <FriendMessage>() // 好友消息 .AddHandler <TempMessage>() // 临时消息 .AddClient <MiraiHttpSession>() // 使用默认的客户端 .Services // 由于 MiraiHttpSession 使用 IOptions<MiraiHttpSessionOptions>, 其作为 Singleton 被注册 // 配置此项将配置基于此 IServiceProvider 全局的连接配置 // 如果你想一个作用域一个配置的话 // 自行做一个实现类, 继承MiraiHttpSession, 构造参数中使用 IOptionsSnapshot<MiraiHttpSessionOptions> // 并将其传递给父类的构造参数 // 然后在每一个作用域中!先!配置好 IOptionsSnapshot<MiraiHttpSessionOptions>, 再尝试获取 IMiraiHttpSession .Configure <MiraiHttpSessionOptions>(options => { options.Host = ip; options.Port = port; // 端口 options.AuthKey = authKey; // 凭据 }) .AddLogging() .BuildServiceProvider(); await using AsyncServiceScope scope = services.CreateAsyncScope(); // 自 .NET 6.0 起才可以如此操作代替上边两句 services = scope.ServiceProvider; IMiraiHttpSession session = services.GetRequiredService <IMiraiHttpSession>(); // 大部分服务都基于接口注册, 请使用接口作为类型解析 await session.ConnectAsync(qqId); // 填入期望连接到的机器人QQ号 Cache.SetTaskAtFixedTime(); IFriendInfo[] IFriendInfos = await session.GetFriendListAsync(); string nickname = "未知"; var self = IFriendInfos.Where(m => m.Id == qqId).FirstOrDefault(); //获取自身昵称失败, 部分QQ号好友中不存在自己? if (self != null) { nickname = self.Name; } ConnectedEvent?.Invoke(true, nickname); BotInfo.QQId = session.QQNumber.Value; BotInfo.IsLogin = true; PluginManager.Connected( BotInfo.QQId, async(targetId, msg) => await session.SendFriendMessageAsync(targetId, await msg.ToMiraiApiHttpMessages(session, UploadTarget.Friend)), async(targetId, msg) => await session.SendGroupMessageAsync(targetId, await msg.ToMiraiApiHttpMessages(session, UploadTarget.Group)), async(targetId, targetGroup, msg) => await session.SendTempMessageAsync(targetId, targetGroup, await msg.ToMiraiApiHttpMessages(session, UploadTarget.Temp)) ); RssWorker.StartRssTask(async(msgs, targetId, groupId) => { if (targetId != -1) { _ = session.SendFriendMessageAsync(targetId, await msgs.ToMiraiApiHttpMessages(session, UploadTarget.Friend)); } else if (groupId != -1) { _ = session.SendGroupMessageAsync(groupId, await msgs.ToMiraiApiHttpMessages(session, UploadTarget.Group)); } }); while (true) { BotInfo.IsLogin = true; if (Console.ReadLine() == "exit") { BotInfo.IsLogin = false; PluginManager.Disconnected(); session.Dispose(); ConnectedEvent?.Invoke(false, ""); break; } Task.Delay(100).Wait(); } } catch (Exception ex) { LogHelper.WriteErrorLog(ex); ConnectedEvent?.Invoke(false, ex.Message); } }
private static void Main(string[] args) { #if DEBUG logger("DEBUG", "WARNING: Running in debug mode."); DEBUGMODE = true; Thread.Sleep(5000); #endif DateTime start = DateTime.Now; //string ipv4_ip = NetworkInfo.GetLocalIpAddress(); MainHolder.logger = logger; bool booted = false; Exception exc = null; #region 读取配置 StreamReader cfile = new StreamReader("config.json"); JObject config = (JObject)JsonConvert.DeserializeObject(cfile.ReadToEnd()); cfile.Close(); host = config["mirai"].Value <string>("server"); me_qq = config["mirai"].Value <long>("user"); key = config["mirai"].Value <string>("key"); try { port = config["mirai"].Value <int>("port"); } catch { port = 8080; } authenti = config["auth"].Value <string>("name"); MainHolder.LiveRoom = config["bili"].Value <int>("roomid"); MainHolder.BiliWatchUIDs = new List <int>(); foreach (JToken j in config["bili"]["uids"]) { MainHolder.BiliWatchUIDs.Add(j.Value <int>()); } MainHolder.useBiliRecFuncs = config["auth"].Value <bool>("rectfunc"); MainHolder.enableNativeFuncs = config["auth"].Value <bool>("nativefuncs"); //如果是本地ip就走本地 //if (host == ipv4_ip && Environment.OSVersion.Platform != PlatformID.Win32NT) //{ // logger("MainThread", "Running on the same server! Using 127.0.0.1 to connect mirai."); // host = "127.0.0.1"; //} #endregion while (true)//故障自动重启 { try { //Console.Title = "ManageBot By Developer_ken - Initializing..."; logger("MainThread", "Pushing up the engine...", ConsoleColor.Black, ConsoleColor.Green); { IServiceProvider services = new ServiceCollection().AddMiraiBaseFramework() // 表示使用基于基础框架的构建器 .AddHandler <tech.msgp.groupmanager.Code.EventHandlers.EventHandler>() .Services .AddDefaultMiraiHttpFramework() // 表示使用 mirai-api-http 实现的构建器 .ResolveParser <tech.msgp.groupmanager.Code.EventHandlers.EventHandler>() // 只提前解析 DynamicPlugin 将要用到的消息解析器 .AddInvoker <MiraiHttpMessageHandlerInvoker>() // 使用默认的调度器 .AddClient <MiraiHttpSession>() // 使用默认的客户端 .Services // 由于 IMiraiHttpSession 使用 IOptions<MiraiHttpSessionOptions>, 其作为 Singleton 被注册 // 配置此项将配置基于此 IServiceProvider 全局的连接配置 // 如果你想一个作用域一个配置的话 // 自行做一个实现类, 继承IMiraiHttpSession, 构造参数中使用 IOptionsSnapshot<MiraiHttpSessionOptions> // 并将其传递给父类的构造参数 // 然后在每一个作用域中!先!配置好 IOptionsSnapshot<MiraiHttpSessionOptions>, 再尝试获取 IMiraiHttpSession .Configure <MiraiHttpSessionOptions>(options => { options.Host = host; options.Port = port; // 端口 options.AuthKey = key; // 凭据 }) .AddLogging() .BuildServiceProvider(); IServiceScope scope = services.CreateScope(); //var x = (IAsyncDisposable)scope; //await using AsyncServiceScope scope = services.CreateAsyncScope(); // 自 .NET 6.0 起才可以如此操作代替上边两句 services = scope.ServiceProvider; IMiraiHttpSession session = services.GetRequiredService <IMiraiHttpSession>(); // 大部分服务都基于接口注册, 请使用接口作为类型解析 session.ConnectAsync(me_qq).Wait(); // 填入期望连接到的机器人QQ号 MainHolder.session = session; } //MainHolder.session.GetFriendListAsync().Wait(); logger("MainThread", "BotAPI is up.", ConsoleColor.Black, ConsoleColor.Green); pool = new pThreadPool(); MainHolder.pool = pool; logger("MainThread", "Threadpool is UP.", ConsoleColor.Black, ConsoleColor.Green); pool.submitWorkload(() => { while (true) { /* * logger("threadpool", "threads= " + pool.min_size + "<" + pool.size + "/" + pool.busythread + "<" + pool.max_size + * " | works= " + pool.queuelen + "<" + pool.queue_max_len + " | errors= " + pool.excepttionlen + "<" + pool.exceptionmaxlen * ); */ Dictionary <Guid, Exception> err = pool.popException(); if (err != null) { foreach (KeyValuePair <Guid, Exception> e in err) { logger("threadpool_exception", "Work-" + e.Key + " -> " + e.Value.Message, ConsoleColor.DarkRed); logger("threadpool_exception", "Work-" + e.Key + " -> " + e.Value.StackTrace, ConsoleColor.DarkRed); } } pool.clearExceptions(); Thread.Sleep(1000); } }); pool.onWorkloadStartProcess += startwork; pool.onWorkloadStopProcess += stopwork; logger("MainThread", "Events registered.", ConsoleColor.Black, ConsoleColor.Green); MainHolder.INIT(config); //string xml = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID=\"35\" templateID=\"1\" action=\"viewMultiMsg\" brief=\"[聊天记录]\" m_resid=\"y0oBW4IOb1T2mMOQXiMI9tajqUkTEioFVMFc66YCia2fQEx2+Sp1Bogtcn80e6R+\" m_fileName=\"6858932750478640422\" tSum=\"34\" sourceMsgId=\"0\" url=\"\" flag=\"3\" adverSign=\"0\" multiMsgFlag=\"0\"><item layout=\"1\" advertiser_id=\"0\" aid=\"0\"><title size=\"34\" maxLines=\"2\" lineSpace=\"12\">群聊的聊天记录</title><title size=\"26\" color=\"#777777\" maxLines=\"4\" lineSpace=\"12\">古小艺: 《迷惑行为》新增1个影像</title><title size=\"26\" color=\"#777777\" maxLines=\"4\" lineSpace=\"12\">柠檬味的海鲜龙: 嗯呢</title><title size=\"26\" color=\"#777777\" maxLines=\"4\" lineSpace=\"12\">一只鸡蛋: 新的机器人核心功能已经快移植好了\n现在是离线状态\n这几天要辛苦大家手动处理这些…</ title >< title size =\"26\" color=\"#777777\" maxLines=\"4\" lineSpace=\"12\">呆 萌呆萌瓜: ohhhhhhh</title><hr hidden=\"false\" style=\"0\" /><summary size=\"26\" color=\"#777777\">查看34条转发消息</summary></item><source name=\"聊天记录\" icon=\"\" action=\"\" appid=\"-1\" /></msg>"; //api.sendXmlMessage(417944217, xml, MsgType.GROUP); //Console.Title = "ManageBot By Developer_ken - Standby"; try { /* * MainHolder.session.GroupMessageEvt += new Event_GroupMessage().GroupMessage; * MainHolder.session.GroupApplyEvt += new GroupEnterRequest().GroupApply; * MainHolder.session.GroupMemberJoinedEvt += new GroupMemberIncrease().GroupMemberJoined; * GroupMemberLeave gleave = new GroupMemberLeave(); * MainHolder.session.GroupMemberKickedEvt += gleave.GroupMemberKicked; * MainHolder.session.GroupMemberPositiveLeaveEvt += gleave.GroupMemberPositiveLeave; * PrivMessageHan pmsg = new PrivMessageHan(); * MainHolder.session.TempMessageEvt += pmsg.TempMessage; * MainHolder.session.FriendMessageEvt += pmsg.FriendMessage; * /* * han.onGroupMessageReceive += new Event_GroupMessage().GroupMessage; * han.onPrivateMessageReceive += new Event_PrivMessage().PrivateMessage; * han.onGroupMemberIncrease += new Event_GroupMemberIncrease().GroupMemberIncrease; * han.onGroupMemberDecrease += new Event_GroupMemberLeave().GroupMemberDecrease; * han.onGroupEnterRequest += new Event_GroupMemberRequest().GroupAddRequest; * han.onGroupMessageSendOkay += onSendSuccess; */ logger("MainThread", "Event Recevier is UP.", ConsoleColor.Black, ConsoleColor.White); } catch (Exception) { logger("MainThread", "Event Recevier FAILED.", ConsoleColor.Black, ConsoleColor.Red); } logger("MainThread", "Stand by. The bot is up and ready to go. Type to set an log filter.", ConsoleColor.Black, ConsoleColor.Green); #if RELEASE MainHolder.broadcaster.BroadcastToAdminGroup("[启动报告]\n" + (DEBUGMODE ? "⚠当前处于调试模式,不适合长期运行⚠\n" : "") + "当前版本:" + codeName + version + "\n" + "启用耗时:" + (DateTime.Now - start).TotalSeconds + "s\n" + "当前授权:" + authenti + "\n" + "授权约束:" + (MainHolder.useBiliRecFuncs ? "" : "[MainHolder.useBiliRecFuncs=False]\n") + (MainHolder.enableNativeFuncs ? "" : "[MainHolder.enableNativeFuncs=False]\n")) ; #endif if (booted) { MainHolder.broadcaster.BroadcastToAdminGroup("严重故障\n机器人遭遇了不可恢复的错误,主线程无法继续运行。为了确保稳定运行," + "主线程已被系统重置。该操作成功完成,程序将会继续运行,但未保存的工作可能已经丢失。\n"); MainHolder.broadcaster.BroadcastToAdminGroup("错误报告\n" + exc.Message + "\nStackTrace >" + exc.StackTrace); } booted = true; while (true) { try { string field = ""; do { ConsoleKeyInfo k = Console.ReadKey(true); if (k.Key == ConsoleKey.Enter) { break; } if (k.Key == ConsoleKey.Backspace) { if (field.Length >= 1) { field = field.Substring(0, field.Length - 1); } continue; } if (k.Key == ConsoleKey.Delete) { field = ""; continue; } field += k.KeyChar; //Console.Title = "input>" + field + " | Press [Enter] to " + (field.Length >= 1 ? "set" : "remove") + " a filter."; } while (true); keyword = field; if (keyword != "") { logger("control", "Filter set.", ConsoleColor.Black, ConsoleColor.Green); //Console.Title = "Filter: " + keyword + " | Press [Enter] to remove filter"; } else { logger("control", "Filter removed.", ConsoleColor.Black, ConsoleColor.Green); //Console.Title = "ManageBot By Developer_ken - Standby"; } } catch { Thread.Sleep(int.MaxValue); } } ; } catch (Exception err) { exc = err; logger("EXCEPTION", "E_FATAL " + err.Message, ConsoleColor.Black, ConsoleColor.Red); logger("EXCEPTION", "STACKTRACE " + err.StackTrace, ConsoleColor.Black, ConsoleColor.Red); //throw; } } }