diff --git a/BarrageGrab/App.config b/BarrageGrab/App.config index b6dd7aa..c298622 100644 --- a/BarrageGrab/App.config +++ b/BarrageGrab/App.config @@ -1,109 +1,107 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BarrageGrab/AppConfig.json b/BarrageGrab/AppConfig.json new file mode 100644 index 0000000..477c69f --- /dev/null +++ b/BarrageGrab/AppConfig.json @@ -0,0 +1,71 @@ +{ + // 配置更改后重启才能生效 + "app": { + "general": { + // 是否隐藏控制台 + "hideConsole": false, + // 显示窗体 + "showWindow": false + }, + "network": { + "proxy": { + // 系统代理端口 + "port": 8827, + // 是否启用系统代理,若设置为false 则需要在程序手动指定代理地址 + "enabled": true, + // 上游代理地址,例如开启了系统代理,但是需要将其他无关请求转发到VPN工具中,例如:127.0.0.1:11223,不要带http:// + "upstreamAddress": "" + }, + "websocket": { + // Websocket监听端口 + "listenPort": 8888, + // true:监听在0.0.0.0,接受任意Ip连接,false:监听在127.0.0.1,仅接受本机连接 + "listenAny": true + } + }, + "filtering": { + // 过滤Websocket数据源进程,可用','进行分隔,程序将会监听以下进程的弹幕信息 + "processFilter": "直播伴侣,douyin,chrome,msedge,QQBrowser,360se,firefox,2345explorer,iexplore", + // 开启内置的域名过滤,设置为false会解包所有https请求,cpu占用很高,建议在无法获取弹幕数据时调整 + "hostNameEnabled": true, + // 已知的弹幕域名列表 ','分隔 用作过滤规则中,凡是webcast开头的域名程序都会自动列入白名单 + "hostNameList": "", + // 要进行过滤的Web房间ID,多个使用','分隔,根据缓存来过滤的,直播伴侣不支持 + "webRoomIds": "" + }, + "barrage": { + // 在控制台输出弹幕 + "printEnabled": true, + // 要在控制台打印的弹幕类型,多个使用','分隔,(空代表不过滤) 1[普通弹幕],2[点赞消息],3[进入直播间],4[关注消息],5[礼物消息],6[统计消息],7[粉丝团消息],8[直播间分享],9[下播] + "printFilter": "", + // 要推送的弹幕消息类型,多个使用','分隔,同上 + "pushFilter": "", + // 弹幕文件日志 + "barrageFileLog": false, + // 要日志记录的弹幕消息类型,多个使用','分隔,同上 + "logFilter": "1,2,4,5,6,7,8", + //轮询配置 + "polling": { + // 强制启用轮询模式获取弹幕 (对于容易断开连接或者更加追求稳定的直播间,可以启用这个开关,虽然响应速度不如WebSocket,但是绝对稳定!) + "enabled": false, + // 弹幕轮询间隔,当 forcePolling 为 true 时生效 (毫秒,1000毫秒=1秒,不建议小于1000毫秒,太小可能会被封IP,值越小,弹幕流越丝滑,对于观众多的直播间可以改小) + "interval": 3000, + // 禁用直播页浏览器脚本缓存 (如果需要确保脚本每次能够正常匹配替换,则启用它,可能会损失一定的页面加载速度) + "disableScriptCache": false + } + }, + "liveCompanion": { + // 直播伴侣exe文件位置(除非程序找不到,否则请留空) + "path": "", + // 直播伴侣进程代理开关 + "hookEnabled": false, + // 进入直播间自动暂停播放 + "autoPause": true + }, + + "comPort": { + // 指定要发送的COM串口 格式:'COM1:9600' 代表用COM1发送,波特率9600,若不需要使用串口抄送服务请留空,(启用后程序会自动生成过滤器模板文件到 scripts/comPortFilter.js) + "config": "" + } + } +} \ No newline at end of file diff --git a/BarrageGrab/AppRuntime.cs b/BarrageGrab/AppRuntime.cs index 390466f..485f78f 100644 --- a/BarrageGrab/AppRuntime.cs +++ b/BarrageGrab/AppRuntime.cs @@ -37,10 +37,10 @@ namespace BarrageGrab static AppRuntime() { - + } - public static void Init() + public static void Init() { WsServer = new WsBarrageServer(); RoomCaches = new RoomCacheManager(); @@ -57,7 +57,7 @@ namespace BarrageGrab var hWnd = WinApi.GetConsoleWindow(); if (hWnd != IntPtr.Zero) { - WinApi.ShowWindow(hWnd, show? WinApi.CmdShow.SW_SHOW:WinApi.CmdShow.SW_HIDE); + WinApi.ShowWindow(hWnd, show ? WinApi.CmdShow.SW_SHOW : WinApi.CmdShow.SW_HIDE); } } @@ -95,7 +95,7 @@ namespace BarrageGrab WebRoomId = webrid, Title = "房间" + webrid }; - var succ = RoomInfoCache.TryAdd(roomid,info); + var succ = RoomInfoCache.TryAdd(roomid, info); if (succ) { OnCache?.Invoke(this, new RoomCacheEventArgs() @@ -130,7 +130,7 @@ namespace BarrageGrab var succ = RoomInfoCache.TryAdd(roomid, roomInfo); if (succ) { - OnCache?.Invoke(this,new RoomCacheEventArgs() + OnCache?.Invoke(this, new RoomCacheEventArgs() { Model = 0, RoomInfo = roomInfo @@ -147,7 +147,7 @@ namespace BarrageGrab public string GetCachedWebRoomid(string roomid) { RoomInfo value; - if (RoomInfoCache.TryGetValue(roomid,out value)) return value.WebRoomId??"未知"; + if (RoomInfoCache.TryGetValue(roomid, out value)) return value.WebRoomId ?? "未知"; return "未知"; } @@ -177,13 +177,13 @@ namespace BarrageGrab public class RoomCacheEventArgs : EventArgs { - public RoomInfo RoomInfo { get; set; } - + public RoomInfo RoomInfo { get; set; } + /// /// 0:添加,1更新,2删除 /// public int Model { get; set; } } - } + } } } diff --git a/BarrageGrab/AppSetting.cs b/BarrageGrab/AppSetting.cs index 2a4d2c4..fb2437a 100644 --- a/BarrageGrab/AppSetting.cs +++ b/BarrageGrab/AppSetting.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Drawing; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using BarrageGrab.Modles.JsonEntity; +using Newtonsoft.Json.Linq; using static System.Configuration.ConfigurationManager; -using System.Drawing; namespace BarrageGrab { @@ -29,7 +31,7 @@ namespace BarrageGrab LogFilter = Enum.GetValues(typeof(PackMsgType)).Cast().Where(w => w > 0).ToArray(); FilterHostName = bool.Parse(AppSettings["filterHostName"].Trim()); HostNameFilter = AppSettings["hostNameFilter"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray(); - UsedProxy = bool.Parse(AppSettings["usedProxy"].Trim()); + UsedProxy = bool.Parse(AppSettings["sysProxy"].Trim()); ListenAny = bool.Parse(AppSettings["listenAny"].Trim()); UpstreamProxy = AppSettings["upstreamProxy"].Trim(); HideConsole = bool.Parse(AppSettings["hideConsole"].Trim()); @@ -53,6 +55,405 @@ namespace BarrageGrab } } + /// + /// 从JSON文件加载配置 + /// + /// JSON配置文件路径 + public void LoadFromJson(string jsonFilePath = null) + { + if(jsonFilePath==null) jsonFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appConfig.json"); + try + { + if(!File.Exists(jsonFilePath)) + { + Logger.LogError($"配置文件不存在: {jsonFilePath}"); + return; + } + + // 读取JSON文件内容(保留注释) + string jsonContent = File.ReadAllText(jsonFilePath); + + // 解析JSON(Newtonsoft.Json支持JSONC格式,会自动忽略注释) + JObject config = JObject.Parse(jsonContent); + + // 从JSON读取应用配置 + var app = config["app"]; + + // 常规配置 + var general = app["general"]; + HideConsole = general["hideConsole"]?.Value() ?? false; + ShowWindow = general["showWindow"]?.Value() ?? false; + + // 网络配置 + var network = app["network"]; + var proxy = network["proxy"]; + ProxyPort = proxy["port"]?.Value() ?? 8827; + UsedProxy = proxy["enabled"]?.Value() ?? true; + UpstreamProxy = proxy["upstreamAddress"]?.Value() ?? string.Empty; + + var websocket = network["websocket"]; + WsProt = websocket["listenPort"]?.Value() ?? 8888; + ListenAny = websocket["listenAny"]?.Value() ?? true; + + // 过滤配置 + var filtering = app["filtering"]; + string processFilterStr = filtering["processFilter"]?.Value() ?? "直播伴侣,douyin,chrome,msedge,QQBrowser,360se,firefox,2345explorer,iexplore"; + ProcessFilter = processFilterStr.Split(','); + FilterHostName = filtering["hostNameEnabled"]?.Value() ?? true; + string hostNameFilterStr = filtering["hostNameList"]?.Value() ?? string.Empty; + HostNameFilter = !string.IsNullOrWhiteSpace(hostNameFilterStr) + ? hostNameFilterStr.Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray() + : new string[0]; + string webRoomIdsStr = filtering["webRoomIds"]?.Value() ?? string.Empty; + WebRoomIds = !string.IsNullOrWhiteSpace(webRoomIdsStr) + ? webRoomIdsStr.Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray() + : new string[0]; + + // 弹幕配置 + var barrage = app["barrage"]; + PrintBarrage = barrage["printEnabled"]?.Value() ?? true; + BarrageLog = barrage["barrageFileLog"]?.Value() ?? false; + var polling = app["barrage"]["polling"]; + ForcePolling = polling["enabled"]?.Value() ?? false; + PollingInterval = polling["interval"]?.Value() ?? 3000; + DisableLivePageScriptCache = polling["disableScriptCache"]?.Value() ?? false; + + // 设置默认过滤器 + PrintFilter = Enum.GetValues(typeof(PackMsgType)).Cast().Where(w => w > 0).ToArray(); + PushFilter = Enum.GetValues(typeof(PackMsgType)).Cast().Where(w => w > 0).ToArray(); + LogFilter = Enum.GetValues(typeof(PackMsgType)).Cast().Where(w => w > 0).ToArray(); + + string printFilterStr = barrage["printFilter"]?.Value() ?? string.Empty; + string pushFilterStr = barrage["pushFilter"]?.Value() ?? string.Empty; + string logFilterStr = barrage["logFilter"]?.Value() ?? "1,2,4,5,6,7,8"; + + // 解析过滤器 + if (!string.IsNullOrWhiteSpace(printFilterStr)) + { + PrintFilter = printFilterStr.Split(',').Select(x => int.Parse(x)).ToArray(); + } + if (!string.IsNullOrWhiteSpace(pushFilterStr)) + { + PushFilter = pushFilterStr.Split(',').Select(x => int.Parse(x)).ToArray(); + } + if (!string.IsNullOrWhiteSpace(logFilterStr)) + { + LogFilter = logFilterStr.Split(',').Select(x => int.Parse(x)).ToArray(); + } + + // 直播伴侣配置 + var liveCompanion = app["liveCompanion"]; + LiveCompanPath = liveCompanion["path"]?.Value() ?? string.Empty; + LiveCompanHookSwitch = liveCompanion["hookEnabled"]?.Value() ?? false; + AutoPause = liveCompanion["autoPause"]?.Value() ?? true; + + // COM端口配置 + var comPort = app["comPort"]; + string comPortStr = comPort["config"]?.Value() ?? string.Empty; + if (!string.IsNullOrWhiteSpace(comPortStr)) + { + try + { + var spit = comPortStr.Split(':'); + ComPort = spit[0].Trim(); + ComBaudRate = int.Parse(spit[1].Trim()); + ComPortSwitch = true; + } + catch (Exception ex) + { + throw new Exception("串口配置格式错误," + ex.Message, ex); + } + } + + Logger.PrintColor("已从JSON配置文件加载设置"); + } + catch (Exception ex) + { + Logger.PrintColor($"从JSON加载配置失败: {ex.Message},将尝试从App.config加载"); + } + } + + /// + /// 从启动参数加载配置 + /// + /// 命令行参数数组 + public void LoadFromCommand(string[] args) + { + if (args == null || args.Length == 0) + { + return; + } + + Logger.PrintColor("正在从命令行参数加载配置..."); + + try + { + for (int i = 0; i < args.Length; i++) + { + string arg = args[i].ToLower().Trim(); + + // 如果参数以-或--开头,则为命令行开关 + if (arg.StartsWith("-") || arg.StartsWith("--")) + { + string paramName = arg.TrimStart('-'); + string paramValue = string.Empty; + bool switchDef = false; + + // 检查下一个参数是否是值(不是以-开头) + if (i + 1 < args.Length && !(args[i + 1].StartsWith("-") || args[i + 1].StartsWith("--"))) + { + paramValue = args[i + 1]; + i++; // 跳过已处理的值参数 + } + else + { + // 对于布尔开关,没有值意味着true + paramValue = "?"; + switchDef = true; + } + + // 根据参数名称设置相应的配置 + switch (paramName) + { + // 网络相关配置 + case "port": + case "wsport": + case "ws-port": + if (int.TryParse(paramValue, out int wsPort)) + { + WsProt = wsPort; + Logger.PrintColor($"WebSocket端口设置为: {wsPort}"); + } + break; + + case "proxy-port": + case "proxyport": + if (int.TryParse(paramValue, out int proxyPort)) + { + ProxyPort = proxyPort; + Logger.PrintColor($"代理端口设置为: {proxyPort}"); + } + break; + + case "upstream": + case "upstream-proxy": + UpstreamProxy = paramValue; + Logger.PrintColor($"上游代理设置为: {paramValue}"); + break; + + case "listen-any": + case "listenany": + if (switchDef) break; + ListenAny = ParseBool(paramValue); + Logger.PrintColor($"监听任意IP设置为: {ListenAny}"); + break; + + case "sysproxy": + case "sys-proxy": + if (switchDef) break; + UsedProxy = ParseBool(paramValue); + Logger.PrintColor($"系统代理设置为: {UsedProxy}"); + break; + + // 过滤相关配置 + case "process-filter": + case "processfilter": + ProcessFilter = paramValue.Split(','); + Logger.PrintColor($"进程过滤器设置为: {paramValue}"); + break; + + case "hostname-filter": + case "hostnamefilter": + HostNameFilter = paramValue.Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray(); + Logger.PrintColor($"域名过滤器设置为: {paramValue}"); + break; + + case "filter-hostname": + case "filterhostname": + if (switchDef) break; + FilterHostName = ParseBool(paramValue); + Logger.PrintColor($"是否启用域名过滤设置为: {FilterHostName}"); + break; + + case "room-ids": + case "roomids": + WebRoomIds = paramValue.Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray(); + Logger.PrintColor($"房间ID过滤器设置为: {paramValue}"); + break; + + // 弹幕相关配置 + case "print": + case "print-barrage": + if (switchDef) break; + PrintBarrage = ParseBool(paramValue); + Logger.PrintColor($"控制台打印弹幕设置为: {PrintBarrage}"); + break; + + case "print-filter": + case "printfilter": + if (!string.IsNullOrWhiteSpace(paramValue)) + { + PrintFilter = paramValue.Split(',').Select(x => int.Parse(x)).ToArray(); + Logger.PrintColor($"控制台打印过滤器设置为: {paramValue}"); + } + break; + + case "push-filter": + case "pushfilter": + if (!string.IsNullOrWhiteSpace(paramValue)) + { + PushFilter = paramValue.Split(',').Select(x => int.Parse(x)).ToArray(); + Logger.PrintColor($"推送过滤器设置为: {paramValue}"); + } + break; + + case "log-filter": + case "logfilter": + if (!string.IsNullOrWhiteSpace(paramValue)) + { + LogFilter = paramValue.Split(',').Select(x => int.Parse(x)).ToArray(); + Logger.PrintColor($"日志过滤器设置为: {paramValue}"); + } + break; + + case "barrage-log": + case "barragelog": + if (switchDef) break; + BarrageLog = ParseBool(paramValue); + Logger.PrintColor($"弹幕文件日志设置为: {BarrageLog}"); + break; + + // 显示和控制台相关 + case "hide-console": + case "hideconsole": + if (switchDef) break; + HideConsole = ParseBool(paramValue); + Logger.PrintColor($"隐藏控制台设置为: {HideConsole}"); + break; + + case "show-window": + case "showwindow": + if (switchDef) break; + ShowWindow = ParseBool(paramValue); + Logger.PrintColor($"显示窗体设置为: {ShowWindow}"); + break; + + // 轮询相关配置 + case "force-polling": + case "forcepolling": + if (switchDef) break; + ForcePolling = ParseBool(paramValue); + Logger.PrintColor($"强制轮询模式设置为: {ForcePolling}"); + break; + + case "polling-interval": + case "pollinginterval": + if (int.TryParse(paramValue, out int interval)) + { + PollingInterval = interval; + Logger.PrintColor($"轮询间隔设置为: {interval}ms"); + } + break; + + case "disable-cache": + case "disablecache": + if (switchDef) break; + DisableLivePageScriptCache = ParseBool(paramValue); + Logger.PrintColor($"禁用脚本缓存设置为: {DisableLivePageScriptCache}"); + break; + + // 直播伴侣相关 + case "auto-pause": + case "autopause": + if (switchDef) break; + AutoPause = ParseBool(paramValue); + Logger.PrintColor($"自动暂停设置为: {AutoPause}"); + break; + + case "livecompan-path": + case "livecompanpath": + LiveCompanPath = paramValue; + Logger.PrintColor($"直播伴侣路径设置为: {paramValue}"); + break; + + case "livecompan-hook": + case "livecompanhook": + if (switchDef) break; + LiveCompanHookSwitch = ParseBool(paramValue); + Logger.PrintColor($"直播伴侣Hook开关设置为: {LiveCompanHookSwitch}"); + break; + + // COM端口相关 + case "com": + case "comport": + if (!string.IsNullOrWhiteSpace(paramValue)) + { + try + { + var parts = paramValue.Split(':'); + if (parts.Length == 2) + { + ComPort = parts[0].Trim(); + ComBaudRate = int.Parse(parts[1].Trim()); + ComPortSwitch = true; + Logger.PrintColor($"COM端口设置为: {ComPort} 波特率: {ComBaudRate}"); + } + } + catch (Exception ex) + { + Logger.LogError($"COM端口配置格式错误: {ex.Message}"); + } + } + break; + + // 配置文件相关 + case "config": + case "jsonconfig": + try + { + if (File.Exists(paramValue)) + { + LoadFromJson(); + Logger.PrintColor($"已从JSON配置文件加载配置: {paramValue}"); + } + else + { + Logger.LogError($"指定的配置文件不存在: {paramValue}"); + } + } + catch (Exception ex) + { + Logger.LogError($"加载JSON配置文件失败: {ex.Message}"); + } + break; + + default: + Logger.LogWarn($"未知的命令行参数: {arg}"); + break; + } + } + } + + Logger.PrintColor("命令行参数配置加载完成"); + } + catch (Exception ex) + { + Logger.LogError($"命令行参数解析错误: {ex.Message}"); + } + } + + /// + /// 解析布尔值字符串 + /// + private bool ParseBool(string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + value = value.ToLower().Trim(); + + return value == "true" || value == "1" || value == "yes" || value == "y" || value == "on"; + } //com串口设置 private void ConfigComPort() @@ -65,7 +466,7 @@ namespace BarrageGrab var spit = comPort.Split(':'); ComPort = spit[0].Trim(); ComBaudRate = int.Parse(spit[1].Trim()); - UseComPortFilter = bool.Parse(AppSettings["useComPortFilter"]); + ComPortSwitch = true; } catch (Exception ex) { @@ -244,7 +645,7 @@ namespace BarrageGrab /// /// 是否启用串口过滤器脚本 /// - public bool UseComPortFilter { get; set; } + public bool ComPortSwitch { get; set; } = false; /// /// 直播伴侣文件位置 diff --git a/BarrageGrab/nlog.config b/BarrageGrab/Configs/nlog.config similarity index 100% rename from BarrageGrab/nlog.config rename to BarrageGrab/Configs/nlog.config diff --git a/BarrageGrab/Forms/FormView.cs b/BarrageGrab/Forms/FormView.cs index 430a38b..0c400a8 100644 --- a/BarrageGrab/Forms/FormView.cs +++ b/BarrageGrab/Forms/FormView.cs @@ -1,8 +1,4 @@ -using BarrageGrab.Forms; -using BarrageGrab.Forms.Models; -using BarrageGrab.Modles.JsonEntity; -using BarrageGrab.Proxy; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -13,6 +9,10 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using BarrageGrab.Forms; +using BarrageGrab.Forms.Models; +using BarrageGrab.Modles.JsonEntity; +using BarrageGrab.Proxy; namespace BarrageGrab { @@ -29,7 +29,7 @@ namespace BarrageGrab InitializeComponent(); barServer.OnPrint += WssService_OnPrint; barServer.Grab.Proxy.OnProxyStatus += Proxy_OnProxyStatus; - AppRuntime.RoomCaches.OnCache += RoomCaches_OnCache; + AppRuntime.RoomCaches.OnCache += RoomCaches_OnCache; } private void InitTabPages() @@ -85,8 +85,8 @@ namespace BarrageGrab private void Ck_CheckedChanged(object sender, EventArgs e) { - CheckBox cbx = sender as CheckBox; - if (cbx == null) return; + CheckBox cbx = sender as CheckBox; + if (cbx == null) return; var selected = GetCheckedBarTypes((FlowLayoutPanel)cbx.Parent); if (cbx.Parent.Parent == this.tabPage_Console) { @@ -96,7 +96,7 @@ namespace BarrageGrab { AppSetting.Current.PushFilter = selected; } - if(cbx.Parent.Parent == this.tabPage_Log) + if (cbx.Parent.Parent == this.tabPage_Log) { AppSetting.Current.LogFilter = selected; } @@ -113,12 +113,12 @@ namespace BarrageGrab if (cbk.Checked && cbk.Name.StartsWith("cbx_bartype_")) { list.Add((int)cbk.Tag); - } + } } return list.OrderBy(o => o).ToArray(); } - private Dictionary GetMsgTypes() + private Dictionary GetMsgTypes() { var dic = new Dictionary(); Type enumType = typeof(PackMsgType); @@ -127,7 +127,7 @@ namespace BarrageGrab { DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute), false); int value = (int)field.GetValue(null); - if (attribute != null && value>0) + if (attribute != null && value > 0) { dic.Add(value, attribute.Description); } @@ -146,7 +146,7 @@ namespace BarrageGrab this.label3.Text = $"房间缓存列表({AppRuntime.RoomCaches.RoomInfoCache.Count})"; list_roomCaches.Items.Add(item); } - })); + })); } private void FormView_Load(object sender, EventArgs e) @@ -165,7 +165,7 @@ namespace BarrageGrab this.Invoke(new Action(() => { this.cbx_enableProxy.Checked = e.Open; - })); + })); } private void WssService_OnPrint(object sender, WsBarrageServer.PrintEventArgs e) @@ -187,7 +187,7 @@ namespace BarrageGrab this.rich_output.Clear(); printCount = 0; } - })); + })); } private void cbx_enableProxy_CheckedChanged(object sender, EventArgs e) @@ -209,7 +209,7 @@ namespace BarrageGrab var selectedItem = list_roomCaches.SelectedItem as RoomCacheItem; if (selectedItem == null) return; - var form = new RoomDetail(selectedItem.RoomInfo); + var form = new RoomDetail(selectedItem.RoomInfo); form.ShowDialog(); } @@ -225,8 +225,8 @@ namespace BarrageGrab catch (Exception ex) { //弹窗提示错误 - MessageBox.Show(ex.Message,"设置失败"); - } + MessageBox.Show(ex.Message, "设置失败"); + } } private void cbx_barrageLog_CheckedChanged(object sender, EventArgs e) @@ -235,5 +235,5 @@ namespace BarrageGrab AppSetting.Current.BarrageLog = checker.Checked; AppSetting.Current.Save(); } - } + } } diff --git a/BarrageGrab/Forms/Models/RoomCacheItem.cs b/BarrageGrab/Forms/Models/RoomCacheItem.cs index 6dad8c2..2deaa6b 100644 --- a/BarrageGrab/Forms/Models/RoomCacheItem.cs +++ b/BarrageGrab/Forms/Models/RoomCacheItem.cs @@ -1,9 +1,9 @@ -using BarrageGrab.Modles; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using BarrageGrab.Modles; namespace BarrageGrab.Forms.Models { @@ -21,7 +21,7 @@ namespace BarrageGrab.Forms.Models public override string ToString() { if (RoomInfo == null) return "NULL"; - return $"({RoomInfo.WebRoomId}){RoomInfo?.Owner?.Nickname??"-1"}"; + return $"({RoomInfo.WebRoomId}){RoomInfo?.Owner?.Nickname ?? "-1"}"; } } } diff --git a/BarrageGrab/Forms/RoomDetail.cs b/BarrageGrab/Forms/RoomDetail.cs index 352b3ce..67358f4 100644 --- a/BarrageGrab/Forms/RoomDetail.cs +++ b/BarrageGrab/Forms/RoomDetail.cs @@ -1,5 +1,4 @@ -using BarrageGrab.Modles; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -8,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using BarrageGrab.Modles; using Newtonsoft.Json; namespace BarrageGrab.Forms @@ -18,7 +18,7 @@ namespace BarrageGrab.Forms { InitializeComponent(); //小驼峰 - var json = JsonConvert.SerializeObject(info,Formatting.Indented,new JsonSerializerSettings() + var json = JsonConvert.SerializeObject(info, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }); diff --git a/BarrageGrab/JsEngine.cs b/BarrageGrab/JsEngine.cs index 0ba724f..0ad2fd9 100644 --- a/BarrageGrab/JsEngine.cs +++ b/BarrageGrab/JsEngine.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; -using System.IO; using Jint; -using Newtonsoft.Json; using Jint.Native; +using Newtonsoft.Json; namespace BarrageGrab { @@ -46,15 +46,45 @@ namespace BarrageGrab } /// - /// 从engine 目录获取js 文件 + /// 从scripts 目录获取js 文件 /// /// /// public static string GetJsFile(string fileName) { - var path = Path.Combine(AppContext.BaseDirectory, "Scripts", "engine", fileName); - if (!File.Exists(path)) throw new FileNotFoundException("不存在的文件", path); - return File.ReadAllText(path, Encoding.UTF8); + var checkDirs = new string[] + { + Path.Combine(AppContext.BaseDirectory, "Scripts"), + Path.Combine(AppContext.BaseDirectory, "scripts"), + }; + + foreach (var dir in checkDirs) + { + if (Directory.Exists(dir)) + { + var path = Path.Combine(dir, fileName); + if (File.Exists(path)) + { + return File.ReadAllText(path, Encoding.UTF8); + } + } + } + return null; + } + + /// + /// 在scripts目录下创建js 文件 + /// + /// + /// + public static void CreateJsFile(string fileName,string content) + { + var dir = Path.Combine(AppContext.BaseDirectory, "scripts"); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + File.WriteAllText(Path.Combine(dir, fileName), content, Encoding.UTF8); } class JsConsole @@ -83,7 +113,7 @@ namespace BarrageGrab Engine eng; public JsEncoder(Engine eng) { - this.eng = eng; + this.eng = eng; } private JsValue ByteConvert(byte[] buff) @@ -165,14 +195,14 @@ namespace BarrageGrab return eng.Invoke("byteToUint8Array", buff); } - public JsValue toNumber(byte[] buff,int startIndex = 0) + public JsValue toNumber(byte[] buff, int startIndex = 0) { return BitConverter.ToDouble(buff, startIndex); } - + public bool toBoolean(byte[] buff, int startIndex = 0) { - return BitConverter.ToBoolean(buff, startIndex); + return BitConverter.ToBoolean(buff, startIndex); } public string toString(byte[] buff, int startIndex = 0) @@ -182,7 +212,7 @@ namespace BarrageGrab public string toString(byte[] buff, int startIndex, int length) { - return BitConverter.ToString(buff, startIndex, length); + return BitConverter.ToString(buff, startIndex, length); } public JsValue getBytes(JsValue value) @@ -190,21 +220,21 @@ namespace BarrageGrab byte[] buff = new byte[0]; switch (value.Type) { - case Jint.Runtime.Types.Empty: + case Jint.Runtime.Types.Empty: case Jint.Runtime.Types.Undefined: case Jint.Runtime.Types.Null: break; case Jint.Runtime.Types.Boolean: buff = BitConverter.GetBytes(value.AsBoolean()); - break; + break; case Jint.Runtime.Types.Number: buff = BitConverter.GetBytes(value.AsNumber()); - break; + break; case Jint.Runtime.Types.BigInt: buff = BitConverter.GetBytes((long)value.AsNumber()); - break; + break; default: - throw new Exception($"不支持该类型转换:" + value.Type); + throw new Exception($"不支持该类型转换:" + value.Type); } return ByteConvert(buff); diff --git a/BarrageGrab/Logger.cs b/BarrageGrab/Logger.cs index 967da2c..3f91796 100644 --- a/BarrageGrab/Logger.cs +++ b/BarrageGrab/Logger.cs @@ -1,22 +1,37 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; -using NLog; using BarrageGrab.Modles.JsonEntity; -using System.IO; +using NLog; namespace BarrageGrab { public static class Logger { - private static NLog.Config.ISetupBuilder builder = LogManager.Setup().LoadConfigurationFromFile("nlog.config"); + private static NLog.Config.ISetupBuilder builder; - private static readonly NLog.Logger logger = builder.GetLogger("*"); + private static NLog.Logger logger; + + static Logger() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + //读取嵌入式资源文件 + var resourceName = assembly.GetManifestResourceNames().FirstOrDefault(s => s.EndsWith("nlog.config")); + if (resourceName == null) + { + throw new Exception("nlog.config 嵌入式资源不存在"); + } + + builder = LogManager.Setup().LoadConfigurationFromAssemblyResource(assembly, resourceName); + logger = builder.GetLogger("*"); + } public static void PrintColor(string message, ConsoleColor foreground = ConsoleColor.White) - { + { var color = Console.ForegroundColor; Console.ForegroundColor = foreground; Console.WriteLine(message); @@ -83,9 +98,10 @@ namespace BarrageGrab { var dir = Path.Combine(AppContext.BaseDirectory, "logs", "弹幕日志"); var room = AppRuntime.RoomCaches.GetCachedWebRoomInfo(msg.RoomId.ToString()); - if(room == null) return; + if (room == null) return; var date = DateTime.Now.ToString("yyyy年MM月dd日直播"); - dir = Path.Combine(dir, $"({room.WebRoomId}){room?.Owner?.Nickname ?? "-1"}", date); + var nickName = SafePathString(room?.Owner?.Nickname ?? "-1"); + dir = Path.Combine(dir, $"({room.WebRoomId}){nickName}", date, "场次" + msg.RoomId.ToString()); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); @@ -107,6 +123,31 @@ namespace BarrageGrab } } + /// + /// 将字符串中的特殊字符转换为'?',使其安全用于文件路径 + /// + /// 输入字符串 + /// 处理后的安全字符串 + public static string SafePathString(string input) + { + //在文件系统中,以下字符通常不允许用于文件名:\ / : * ? " < > | + if (string.IsNullOrEmpty(input)) + return input; + + // 文件系统中不允许的字符 + char[] invalidChars = Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).Distinct().ToArray(); + + StringBuilder result = new StringBuilder(input.Length); + foreach (char c in input) + { + result.Append(invalidChars.Contains(c) ? '?' : c); + } + + return result.ToString(); + } + + + private static string LogText(Msg msg, PackMsgType barType) { var rinfo = AppRuntime.RoomCaches.GetCachedWebRoomInfo(msg.RoomId.ToString()); diff --git a/BarrageGrab/Modles/BusinessExecption.cs b/BarrageGrab/Modles/BusinessExecption.cs index 6771c41..b8aa6e5 100644 --- a/BarrageGrab/Modles/BusinessExecption.cs +++ b/BarrageGrab/Modles/BusinessExecption.cs @@ -31,7 +31,7 @@ namespace BarrageGrab.Modles this.Code = -1; } - public BusinessExecption(string msg,object data) : base(msg) + public BusinessExecption(string msg, object data) : base(msg) { this.ErrorTarget = data; } diff --git a/BarrageGrab/Modles/JsonEntity/BarrageMessages.cs b/BarrageGrab/Modles/JsonEntity/BarrageMessages.cs index 9aeaaee..a7d23fc 100644 --- a/BarrageGrab/Modles/JsonEntity/BarrageMessages.cs +++ b/BarrageGrab/Modles/JsonEntity/BarrageMessages.cs @@ -8,8 +8,8 @@ namespace BarrageGrab.Modles.JsonEntity public enum PackMsgType { [Description("无")] - 无 = 0, - [Description("消息")] + 无 = 0, + [Description("消息")] 弹幕消息 = 1, [Description("点赞")] 点赞消息 = 2, @@ -71,8 +71,8 @@ namespace BarrageGrab.Modles.JsonEntity /// /// 消息类型 /// - public PackMsgType Type { get; set; } - + public PackMsgType Type { get; set; } + /// /// 进程名 /// @@ -88,7 +88,7 @@ namespace BarrageGrab.Modles.JsonEntity } - public BarrageMsgPack(string data, PackMsgType type,string processName) + public BarrageMsgPack(string data, PackMsgType type, string processName) { Data = data; Type = type; @@ -304,8 +304,8 @@ namespace BarrageGrab.Modles.JsonEntity /// /// 抖币价格 /// - public int DiamondCount { get; set; } - + public int DiamondCount { get; set; } + /// /// 该礼物是否可连击 /// diff --git a/BarrageGrab/Modles/JsonEntity/Command.cs b/BarrageGrab/Modles/JsonEntity/Command.cs index b25b4d3..8b3e6b7 100644 --- a/BarrageGrab/Modles/JsonEntity/Command.cs +++ b/BarrageGrab/Modles/JsonEntity/Command.cs @@ -16,7 +16,7 @@ namespace BarrageGrab.Modles.JsonEntity /// /// 空指令 /// - None = 0, + None = 0, /// /// 安全关闭程序 diff --git a/BarrageGrab/Modles/RoomInfo.cs b/BarrageGrab/Modles/RoomInfo.cs index ceae585..f778e9c 100644 --- a/BarrageGrab/Modles/RoomInfo.cs +++ b/BarrageGrab/Modles/RoomInfo.cs @@ -4,8 +4,8 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace BarrageGrab.Modles { @@ -659,7 +659,7 @@ namespace BarrageGrab.Modles } - + info = dto; return Tuple.Create(0, "succ"); @@ -710,7 +710,7 @@ namespace BarrageGrab.Modles SecUid = roomOwner["sec_uid"].ToString(), HeadUrl = roomOwner["avatar_thumb"]["url_list"].Values()?.FirstOrDefault() ?? "", FollowStatus = roomOwner["follow_info"]["follow_status"].Value() - }; + }; } info = dto; diff --git a/BarrageGrab/ObjectExtension.cs b/BarrageGrab/ObjectExtension.cs index a6f76ea..3876996 100644 --- a/BarrageGrab/ObjectExtension.cs +++ b/BarrageGrab/ObjectExtension.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using System.Web; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using static ProtoBuf.Serializer; namespace BarrageGrab { @@ -30,13 +31,13 @@ namespace BarrageGrab /// /// /// - public static string ToJson(this object obj,bool format = false) + public static string ToJson(this object obj, bool format = false) { if (obj is string) { return obj.ToString(); } - return JsonConvert.SerializeObject(obj, format?Formatting.Indented:Formatting.None); + return JsonConvert.SerializeObject(obj, format ? Formatting.Indented : Formatting.None); } /// @@ -135,7 +136,7 @@ namespace BarrageGrab } return cookies; } - + /// /// 追加另一个字典 /// @@ -158,11 +159,11 @@ namespace BarrageGrab else { res.Add(item.Key, item.Value); - } + } } return res; } - + /// /// 转换为Cookie对象列表 @@ -359,4 +360,75 @@ namespace BarrageGrab } } + + /// + /// 模式匹配相关扩展方法 + /// + public static class PatternMatchExtensions + { + /// + /// 判断是否匹配任意一个通配符 + /// + /// 要匹配的字符串 + /// 通配符表达式数组 + /// 是否匹配 + public static bool WildcardMatchAny(this string str, params string[] patterns) + { + return patterns.Any(x => x.WildcardMatch(str)); + } + + /// + /// 判断是否匹配通配符 + /// + /// 通配符表达式 + /// 要匹配的字符串 + /// 是否忽略大小写 + /// 是否匹配 + public static bool WildcardMatch(this string wildcard, string s, bool ignoreCase = false) + { + return wildcard.WildcardMatch(s, 0, 0, ignoreCase); + } + + // 递归匹配通配符 + private static bool WildcardMatch(this string wildcard, string s, int wildcardIndex, int sIndex, bool ignoreCase) + { + while (wildcardIndex != wildcard.Length) + { + char c = wildcard[wildcardIndex]; + switch (c) + { + case '*': + if (wildcardIndex == wildcard.Length - 1) + { + return true; + } + + return Enumerable.Range(sIndex, s.Length - sIndex).Any((int i) => wildcard.WildcardMatch(s, wildcardIndex + 1, i, ignoreCase)); + default: + { + char c2 = (ignoreCase ? char.ToLower(c) : c); + if (s.Length == sIndex) + { + return false; + } + + char c3 = (ignoreCase ? char.ToLower(s[sIndex]) : s[sIndex]); + if (c2 != c3) + { + return false; + } + + break; + } + case '?': + break; + } + + wildcardIndex++; + sIndex++; + } + + return sIndex == s.Length; + } + } } diff --git a/BarrageGrab/Program.cs b/BarrageGrab/Program.cs index 8752188..38f8646 100644 --- a/BarrageGrab/Program.cs +++ b/BarrageGrab/Program.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Net.Sockets; using System.Net; +using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using System.IO; -using Jint.Runtime; using System.Windows.Forms; +using Jint.Runtime; namespace BarrageGrab { @@ -19,6 +19,7 @@ namespace BarrageGrab static bool formExited = false; static WinApi.ControlCtrlDelegate controlCtr = ControlCtrlHandle; static Mutex mutex = new Mutex(false, "DyBarrageGrab"); + static void Main(string[] args) { if (!mutex.WaitOne(TimeSpan.Zero, true)) @@ -37,18 +38,18 @@ namespace BarrageGrab catch (Exception ex) { Logger.LogError(ex, $"程序初始化错误,{ex.Message}"); - MessageBox.Show(ex.Message, "程序初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(ex.Message, "程序初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error); exited = true; } while (!exited) - { + { Thread.Sleep(500); } if (!AppRuntime.WsServer.IsDisposed) { - AppRuntime.WsServer.Dispose(); + AppRuntime.WsServer.Dispose(); } Logger.PrintColor("服务器已关闭..."); diff --git a/BarrageGrab/Properties/AssemblyInfo.cs b/BarrageGrab/Properties/AssemblyInfo.cs index 6d1e8b2..87fe95c 100644 --- a/BarrageGrab/Properties/AssemblyInfo.cs +++ b/BarrageGrab/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; +using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -11,7 +11,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("小白不圆个人工作室")] [assembly: AssemblyProduct("抖音弹幕监听器")] -[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,6 +33,6 @@ using System.Runtime.InteropServices; //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.7.7")] -[assembly: AssemblyFileVersion("2.7.7")] +[assembly: AssemblyVersion("2.7.9")] +[assembly: AssemblyFileVersion("2.7.9")] [assembly: NeutralResourcesLanguage("zh-CN")] diff --git a/BarrageGrab/Proxy/ISystemProxy.cs b/BarrageGrab/Proxy/ISystemProxy.cs index 8626e7e..a3edcb1 100644 --- a/BarrageGrab/Proxy/ISystemProxy.cs +++ b/BarrageGrab/Proxy/ISystemProxy.cs @@ -1,6 +1,6 @@ using System; -using System.Reflection; using System.Collections.Generic; +using System.Reflection; using BarrageGrab.Proxy.ProxyEventArgs; namespace BarrageGrab.Proxy @@ -42,5 +42,5 @@ namespace BarrageGrab.Proxy /// 开始监听 /// void Start(); - } + } } \ No newline at end of file diff --git a/BarrageGrab/Proxy/ProxyEventArgs/HttpResponseEventArgs.cs b/BarrageGrab/Proxy/ProxyEventArgs/HttpResponseEventArgs.cs index 82f1b5c..25a5b5d 100644 --- a/BarrageGrab/Proxy/ProxyEventArgs/HttpResponseEventArgs.cs +++ b/BarrageGrab/Proxy/ProxyEventArgs/HttpResponseEventArgs.cs @@ -30,7 +30,7 @@ namespace BarrageGrab.Proxy.ProxyEventArgs /// /// 域名 /// - public string HostName { get; set; } + public string HostName { get; set; } /// /// 有效载荷 diff --git a/BarrageGrab/Proxy/ProxyEventArgs/SystemProxyChangeEventArgs.cs b/BarrageGrab/Proxy/ProxyEventArgs/SystemProxyChangeEventArgs.cs index 2a00689..67cc2c5 100644 --- a/BarrageGrab/Proxy/ProxyEventArgs/SystemProxyChangeEventArgs.cs +++ b/BarrageGrab/Proxy/ProxyEventArgs/SystemProxyChangeEventArgs.cs @@ -6,11 +6,11 @@ using System.Threading.Tasks; namespace BarrageGrab.Proxy.ProxyEventArgs { - public class SystemProxyChangeEventArgs:EventArgs + public class SystemProxyChangeEventArgs : EventArgs { /// /// True 以开启:False: 已关闭 /// - public bool Open { get; set; } + public bool Open { get; set; } } } diff --git a/BarrageGrab/Proxy/SystemProxy.cs b/BarrageGrab/Proxy/SystemProxy.cs index 0920319..1bd20d6 100644 --- a/BarrageGrab/Proxy/SystemProxy.cs +++ b/BarrageGrab/Proxy/SystemProxy.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; -using Microsoft.Win32; -using System.Diagnostics; using BarrageGrab.Proxy.ProxyEventArgs; +using Microsoft.Win32; namespace BarrageGrab.Proxy { @@ -42,7 +42,7 @@ namespace BarrageGrab.Proxy public abstract void Start(); - public abstract void SetUpstreamProxy(string addr); + public abstract void SetUpstreamProxy(string addr); //https://live.douyin.com/webcast/gift/list/ [礼物数据接口] @@ -109,7 +109,7 @@ namespace BarrageGrab.Proxy RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); registry.SetValue("ProxyEnable", 1); registry.SetValue("ProxyServer", $"127.0.0.1:{ProxyPort}"); - + OnProxyStatus?.Invoke(this, new SystemProxyChangeEventArgs() { Open = true @@ -128,7 +128,7 @@ namespace BarrageGrab.Proxy { Open = false }); - } + } public static bool ProxyIsOpen() { diff --git a/BarrageGrab/Proxy/TitaniumProxy.cs b/BarrageGrab/Proxy/TitaniumProxy.cs index 32b5c65..cc2bb43 100644 --- a/BarrageGrab/Proxy/TitaniumProxy.cs +++ b/BarrageGrab/Proxy/TitaniumProxy.cs @@ -1,31 +1,31 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.Security; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Security.Policy; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BarrageGrab.Modles; using BarrageGrab.Modles.JsonEntity; using BarrageGrab.Proxy.ProxyEventArgs; -using Newtonsoft.Json.Linq; +using BrotliSharpLib; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NLog.LayoutRenderers; using Titanium.Web.Proxy; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.StreamExtended.Network; -using System.Security.Cryptography.X509Certificates; -using System.Security.Cryptography; using HtmlDocument = HtmlAgilityPack.HtmlDocument; -using System.Net.Http; -using System.Text; -using BrotliSharpLib; -using System.IO.Compression; -using System.Collections.Concurrent; -using System.Security.Policy; -using NLog.LayoutRenderers; namespace BarrageGrab.Proxy { @@ -90,17 +90,18 @@ namespace BarrageGrab.Proxy proxyServer.CertificateManager.CertificateValidDays = 365 * 10; proxyServer.CertificateManager.SaveFakeCertificates = true; + proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.DefaultWindows; + proxyServer.CertificateManager.OverwritePfxFile = false; proxyServer.CertificateManager.RootCertificate = GetCert(); if (proxyServer.CertificateManager.RootCertificate == null) { Logger.PrintColor("正在进行证书安装,需要信任该证书才可进行https解密,若有提示请确定"); proxyServer.CertificateManager.CreateRootCertificate(); } - - //信任证书 - proxyServer.CertificateManager.CreateRootCertificate(); proxyServer.CertificateManager.TrustRootCertificate(true); + //https://github.com/justcoding121/titanium-web-proxy/issues/828 + proxyServer.ServerCertificateValidationCallback += ProxyServer_ServerCertificateValidationCallback; //proxyServer.BeforeRequest += ProxyServer_BeforeRequest; proxyServer.BeforeResponse += ProxyServer_BeforeResponse; @@ -307,7 +308,7 @@ namespace BarrageGrab.Proxy { lock (rinfoRequestings) rinfoRequestings.Add(webroomid); //查询后会自动缓存 - DyServer.GetRoomInfoForApi(webroomid, cookie).ContinueWith(t => + DyApiHelper.GetRoomInfoForApi(webroomid, cookie).ContinueWith(t => { var rinfo = t.Result; //限制只尝试一次 @@ -332,22 +333,6 @@ namespace BarrageGrab.Proxy } } - /// - /// 获取配置的注入的js - /// - /// - /// - private string GetInjectScript(string name) - { - //获取exe所在目录路径 - var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts", "inject", name + ".js"); - if (File.Exists(path)) - { - return File.ReadAllText(path); - } - return null; - } - // Hook 直播页面 private async Task HookPageAsync(SessionEventArgs e) { @@ -376,8 +361,7 @@ namespace BarrageGrab.Proxy { string webrid = liveRoomMactch.Groups[1].Value; //获取直播页注入js - string liveRoomInjectScript = GetInjectScript("livePage"); - + string liveRoomInjectScript = EmbResource.GetFileContent("livePage.js"); //注入上下文变量; var scriptContext = BuildContext(new Dictionary() { @@ -434,7 +418,7 @@ namespace BarrageGrab.Proxy script.InnerHtml = liveRoomInjectScript; body.AppendChild(script); html = doc.DocumentNode.OuterHtml; - Logger.PrintColor($"直播页{urlNoQuery},用户脚本已成功注入!\n", ConsoleColor.Green); + Logger.LogTrace($"直播页{urlNoQuery},用户脚本已成功注入!\n"); } } catch (Exception ex) @@ -458,7 +442,7 @@ namespace BarrageGrab.Proxy if (liveHomeMatch.Success) { //获取直播页注入js - string liveHoomInjectScript = GetInjectScript("livePage"); + string liveHoomInjectScript = EmbResource.GetFileContent("livePage.js"); //注入上下文变量; var scriptContext = BuildContext(new Dictionary() @@ -548,21 +532,20 @@ namespace BarrageGrab.Proxy if (!contentType.Trim().ToLower().Contains("application/javascript")) return; if (e.HttpClient.Response.StatusCode != 200) return; + //https://lf-webcast-platform.bytetos.com/obj/webcast-platform-cdn/webcast/douyin_live/chunks/island_a74ce.b55095a0.js //判断响应内容是否为js application/javascript - if (hostname == SCRIPT_HOST - //这俩个进程不需要注入 - && processName != "直播伴侣" && processName != "douyin" + if (processName != "直播伴侣" && processName != "douyin" && fileName.StartsWith("island") ) { var js = await e.GetResponseBodyAsString(); - var reg2 = new Regex(@"if\(!\((?\d,\w{1,2})\.DJ\)\(\)&&"); - var match = reg2.Match(js); + var reg = new Regex(@"if\(!\(\d{1,},\w{1,}\.DJ\)\(\).+\w{1,}\.includes\(""live""\)\)\)\{"); + var match = reg.Match(js); if (match.Success) { - js = reg2.Replace(js, "if(false &&"); + js = reg.Replace(js, "if(false){"); e.SetResponseBodyString(js); - Logger.PrintColor($"已成功绕过页面无操作检测\n", ConsoleColor.Green); + Logger.PrintColor($"已成功绕过JS页面无操作检测 {urlNoQuery}\n", ConsoleColor.Green); } } @@ -627,6 +610,8 @@ namespace BarrageGrab.Proxy SCRIPT_HOST, LIVE_HOST, WEBCAST_AMEMV_HOST , //直播伴侣开播请求地址 + "*-webcast-platform.bytetos.com", //新的脚本地址 + "*webcast*" //所有带webcast的域名 }; if (decryptSsls.Contains(hostname)) @@ -634,6 +619,11 @@ namespace BarrageGrab.Proxy return true; } + if (hostname.WildcardMatchAny(decryptSsls)) + { + return true; + } + hostname = hostname.Trim().ToLower(); return base.CheckHost(hostname); } @@ -648,7 +638,7 @@ namespace BarrageGrab.Proxy var processid = args.HttpClient.ProcessId.Value; List messageData = new List(); - + try { foreach (var frame in args.WebSocketDecoderReceive.Decode(e.Buffer, e.Offset, e.Count)) diff --git a/BarrageGrab/Scripts/inject/livePage.js b/BarrageGrab/Scripts/inject/livePage.js index 0f0220d..1192a18 100644 --- a/BarrageGrab/Scripts/inject/livePage.js +++ b/BarrageGrab/Scripts/inject/livePage.js @@ -6,35 +6,9 @@ * 正则: @".*:\/\/live.douyin\.com\/\d+" **/ -/*注入脚本 定时触发全局keydown,避免直播流被无操作阻断*/ - -// 创建一个虚拟的按键事件 -const simulatedEvent = new KeyboardEvent('keydown', { - key: 'Virtual_Alt', - keyCode: 18, //alt - bubbles: true, - cancelable: true -}); - -// 获取要分派事件的目标元素,例如文档的根元素 -const targetElement = document.documentElement; - -// 设置定时器,每隔60秒模拟一次按键事件 -setInterval(() => { - targetElement.dispatchEvent(simulatedEvent); -}, 1000 * 60); - -// 订阅 keydown 事件 (Debug) -targetElement.addEventListener('keydown', event => { - if (event.key == "Virtual_Alt") { - console.log('[防挂机处理] 按下了', event.key); - } -}); - /*禁止关闭 Websocket*/ WebSocket.prototype.close = function () { return; } - /*判断是否自动关闭 */ //获取路由参数 function getQueryString(name) { diff --git a/BarrageGrab/ComPortServer.cs b/BarrageGrab/Server/ComPortServer.cs similarity index 81% rename from BarrageGrab/ComPortServer.cs rename to BarrageGrab/Server/ComPortServer.cs index f328ea1..9ff1c16 100644 --- a/BarrageGrab/ComPortServer.cs +++ b/BarrageGrab/Server/ComPortServer.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; +using System.IO.Ports; using System.Linq; using System.Text; using System.Threading.Tasks; -using System.IO.Ports; -using Jint; -using Jint.Runtime; using System.Windows.Forms; +using Jint; using Jint.Native; +using Jint.Runtime; namespace BarrageGrab { @@ -22,20 +23,30 @@ namespace BarrageGrab static ComPortServer() { - if (AppSetting.Current.UseComPortFilter) + if (!AppSetting.Current.ComPortSwitch) return; + + jsEngine = JsEngine.CreateNewEngine(); + + var jsFile = JsEngine.GetJsFile("comPortFilter.js"); + + //生成模板文件 + if (jsFile == null) { - jsEngine = JsEngine.CreateNewEngine(); - var jsFile = JsEngine.GetJsFile("comPortFilter.js"); - try - { - jsEngine.Execute(jsFile); - } - catch (Exception ex) - { - Logger.LogError(ex, $"comPortFilter.js 执行错误: {ex.Message}"); - throw; - } + jsFile = EmbResource.GetFileContent("comPortFilter.js"); + JsEngine.CreateJsFile("comPortFilter.js", jsFile); + Logger.PrintColor($"已生成串口脚本模板文件 scripts/comPortFilter.js,请按个人需求修改", ConsoleColor.Yellow); } + + try + { + jsEngine.Execute(jsFile); + } + catch (Exception ex) + { + Logger.LogError(ex, $"comPortFilter.js 执行错误: {ex.Message}"); + throw; + } + } public ComPortServer(WsBarrageServer server) @@ -64,7 +75,7 @@ namespace BarrageGrab if (e == null) return; if (e.Message == null) return; - if (!AppSetting.Current.UseComPortFilter) + if (!AppSetting.Current.ComPortSwitch) { var json = e.ToJson() + "\r\n"; var jbuff = Encoding.UTF8.GetBytes(json); @@ -81,7 +92,7 @@ namespace BarrageGrab catch (Exception ex) { Logger.LogError(ex, $"串口过滤器 onPackData 执行错误:{ex.Message}"); - return; + return; } if (result == null) return; @@ -146,7 +157,7 @@ namespace BarrageGrab sendPort.Close(); sendPort.Dispose(); sendPort = null; - throw ex; + Logger.LogError(ex, $"串口打开失败, {ex.Message}"); } } diff --git a/BarrageGrab/WsBarrageServer.cs b/BarrageGrab/Server/WsBarrageServer.cs similarity index 95% rename from BarrageGrab/WsBarrageServer.cs rename to BarrageGrab/Server/WsBarrageServer.cs index 3bc11dc..bf6e502 100644 --- a/BarrageGrab/WsBarrageServer.cs +++ b/BarrageGrab/Server/WsBarrageServer.cs @@ -1,23 +1,23 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Drawing; +using System.Dynamic; +using System.IO; +using System.IO.Compression; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.WebSockets; using System.Text; using System.Threading.Tasks; using System.Timers; -using Fleck; -using Newtonsoft.Json; using BarrageGrab.Modles; -using Newtonsoft.Json.Linq; -using System.Net.Http; -using System.IO.Compression; -using System.IO; using BarrageGrab.Modles.JsonEntity; using BarrageGrab.Modles.ProtoEntity; -using System.Drawing; -using System.Collections.Concurrent; -using System.Dynamic; +using Fleck; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using static BarrageGrab.Modles.ProtoEntity.Image; using static BarrageGrab.Modles.WebCastGift; @@ -176,6 +176,7 @@ namespace BarrageGrab return obj.GetType().GetProperty(propertyName) != null; } + //创建消息对象 private T CreateMsg(dynamic msg) where T : Msg, new() { var roomid = msg.Common.roomId.ToString(); @@ -220,10 +221,10 @@ namespace BarrageGrab if (isAdmin) { - text+= " [管理员]"; + text += " [管理员]"; } - if(isAnchor) + if (isAnchor) { text += " [主播]"; } @@ -286,7 +287,7 @@ namespace BarrageGrab { if (msg == null) return; var roomInfo = AppRuntime.RoomCaches.GetCachedWebRoomInfo(msg.RoomId.ToString()); - if (roomInfo != null && roomInfo.Owner!=null) + if (roomInfo != null && roomInfo.Owner != null) { msg.Onwer = new RoomAnchorInfo() { @@ -580,29 +581,7 @@ namespace BarrageGrab try { var cmdPack = JsonConvert.DeserializeObject(message); - if (cmdPack == null) return; - - switch (cmdPack.Cmd) - { - case CommandCode.Close: - this.Dispose(); - break; - case CommandCode.EnableProxy: - { - var enable = (bool)cmdPack.Data; - if (enable) - this.grab.Proxy.RegisterSystemProxy(); - else - this.grab.Proxy.CloseSystemProxy(); - break; - } - case CommandCode.DisplayConsole: - { - var display = (bool)cmdPack.Data; - AppRuntime.DisplayConsole(display); - break; - } - } + if (cmdPack == null) return; } catch (Exception) { } }; diff --git a/BarrageGrab/WssBarrageGrab.cs b/BarrageGrab/Server/WssBarrageGrab.cs similarity index 100% rename from BarrageGrab/WssBarrageGrab.cs rename to BarrageGrab/Server/WssBarrageGrab.cs index 70162f7..8525b71 100644 --- a/BarrageGrab/WssBarrageGrab.cs +++ b/BarrageGrab/Server/WssBarrageGrab.cs @@ -5,10 +5,10 @@ using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; -using ProtoBuf; +using BarrageGrab.Modles.ProtoEntity; using BarrageGrab.Proxy; using BarrageGrab.Proxy.ProxyEventArgs; -using BarrageGrab.Modles.ProtoEntity; +using ProtoBuf; namespace BarrageGrab { diff --git a/BarrageGrab/DyServer.cs b/BarrageGrab/Utility/DyApiHelper.cs similarity index 98% rename from BarrageGrab/DyServer.cs rename to BarrageGrab/Utility/DyApiHelper.cs index 4ee203c..af96ddb 100644 --- a/BarrageGrab/DyServer.cs +++ b/BarrageGrab/Utility/DyApiHelper.cs @@ -15,7 +15,7 @@ namespace BarrageGrab /// /// 抖音服务 /// - internal class DyServer + internal class DyApiHelper { /// /// 获取直播礼物列表 @@ -55,7 +55,7 @@ namespace BarrageGrab Logger.LogError($"礼物信息从服务器请求失败: {ex.Message}"); return null; } - } + } /// /// 获取房间信息,通过原生接口 @@ -69,7 +69,7 @@ namespace BarrageGrab string url = "https://live.douyin.com/webcast/room/web/enter/"; var client = new HttpClient(); - + var uri = new UriBuilder(url); var dict = new Dictionary(); diff --git a/BarrageGrab/Utility/EmbResource.cs b/BarrageGrab/Utility/EmbResource.cs new file mode 100644 index 0000000..5832abc --- /dev/null +++ b/BarrageGrab/Utility/EmbResource.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BarrageGrab +{ + /// + /// 资源文件 + /// + public static class EmbResource + { + static Dictionary cache = new Dictionary(); + + /// + /// 获取 嵌入式资源文件内容 + /// + /// + /// + /// + public static string GetFileContent(string fileName) + { + if(cache.ContainsKey(fileName)) + { + return cache[fileName]; + } + + Assembly assembly = Assembly.GetExecutingAssembly(); + //读取嵌入式资源文件 + var resourceName = assembly.GetManifestResourceNames().FirstOrDefault(s => s.EndsWith(fileName)); + if (resourceName == null) + { + throw new Exception($"{fileName} 嵌入式资源不存在"); + } + + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + { + using (StreamReader reader = new StreamReader(stream,Encoding.UTF8)) + { + var text = reader.ReadToEnd(); + cache.Add(fileName, text); + return text; + } + } + } + } +} diff --git a/BarrageGrab/LiveCompanHelper.cs b/BarrageGrab/Utility/LiveCompanHelper.cs similarity index 99% rename from BarrageGrab/LiveCompanHelper.cs rename to BarrageGrab/Utility/LiveCompanHelper.cs index 780157a..2c20da1 100644 --- a/BarrageGrab/LiveCompanHelper.cs +++ b/BarrageGrab/Utility/LiveCompanHelper.cs @@ -1,18 +1,18 @@ -using HtmlAgilityPack; -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using System.Runtime.InteropServices; +using HtmlAgilityPack; using IWshRuntimeLibrary; -using File = System.IO.File; using Microsoft.Win32; -using System.Diagnostics; using Newtonsoft.Json; -using System.Runtime.InteropServices.ComTypes; +using File = System.IO.File; namespace BarrageGrab { @@ -219,7 +219,7 @@ namespace BarrageGrab Logger.LogInfo($"直播伴侣文件改动检测点1已拦截"); } - + checkReg = new Regex(@"if\(\(0,\w.integrityCheckReport\)\(\w\),!\w\.ok\)"); if (checkReg.IsMatch(content)) { diff --git a/BarrageGrab/WinApi.cs b/BarrageGrab/Utility/WinApi.cs similarity index 98% rename from BarrageGrab/WinApi.cs rename to BarrageGrab/Utility/WinApi.cs index 927dad2..0cb42f0 100644 --- a/BarrageGrab/WinApi.cs +++ b/BarrageGrab/Utility/WinApi.cs @@ -28,8 +28,6 @@ namespace BarrageGrab internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint mode); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode); - - ///If the function fails, the return value is NULL. [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); diff --git a/BarrageGrab/WssBarrageService.csproj b/BarrageGrab/WssBarrageService.csproj index 98ae325..42c0452 100644 --- a/BarrageGrab/WssBarrageService.csproj +++ b/BarrageGrab/WssBarrageService.csproj @@ -63,6 +63,9 @@ Properties\app.manifest + + logo.ico + ..\packages\Acornima.1.1.0\lib\net462\Acornima.dll @@ -70,9 +73,6 @@ ..\packages\Portable.BouncyCastle.1.8.8\lib\net40\BouncyCastle.Crypto.dll - - ..\packages\BouncyCastle.Cryptography.2.0.0\lib\net461\BouncyCastle.Cryptography.dll - ..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll @@ -206,11 +206,11 @@ + - ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll @@ -221,8 +221,9 @@ - - + + + Form @@ -237,7 +238,7 @@ RoomDetail.cs - + @@ -254,27 +255,25 @@ - - + + - + - - Always - + + WssBarrageService + + - - PreserveNewest - - - Always - + + + @@ -318,4 +317,12 @@ + + if "$(ConfigurationName)" == "Release" ( + del /q "$(TargetDir)*.xml" + del /q "$(TargetDir)*.pdb" + del /q "$(TargetDir)*.pfx" + rmdir /s /q "$(TargetDir)crts" +) + \ No newline at end of file diff --git a/BarrageGrab/logo.ico b/BarrageGrab/logo.ico new file mode 100644 index 0000000..ac8c450 Binary files /dev/null and b/BarrageGrab/logo.ico differ diff --git a/README.md b/README.md index e8a57d7..3c2da77 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,23 @@ ## ⛳近期更新 +2025-04-15 v2.7.9(beta) +1. 大幅优化了程序的体积大小 +2. 默认移除了Script目录,以及其他不必要的文件,但如果开启了串口推送开关,则会自动生成过滤器模板文件 +3. 添加了应用图标 + +2025-04-09 v2.7.8 +1. 修复页面无操作检测拦截失效的问题,因为更新了脚本 +2. 修复弹幕日志因主播昵称的特殊字符导致无法写入的问题 + 2025-02-11 v2.7.7 1. 添加 `Appid` 字段(疑似用户看直播的APP标识) 和 `EnterTipType` 字段(用于获知用户进入直播间的方式,已知0为普通进入,6为通过分享进入) - 2. User字段新增 `IsAdmin` 和 `IsAnchor` 字段,用于区分是否为管理员或者主播的消息 - 3. web在中途监听弹幕,且直播间弹幕链接已降级为轮询的情况下,解决 `webroomid` 以及房间信息丢失问题 - 4. 修复直播伴侣更新后,无法定位到exe位置的问题 - 5. 优化部分其他已知问题 - - 2024-09-28 v2.7.6(beta) 1. **[重要更新]** 支持免系统代理监听直播伴侣,需要在配置文件关闭`useproxy`选项以及打开`liveCompanHookSwitch`选项,配置后监听web直播间可参考实践指南新部分 @@ -46,38 +49,6 @@ 3. 修复了窗体初始化因控件可能导致的句柄出错的问题 4. 修复在使用窗体的情况下,因跨线程调用抛出异常导致的弹幕断连问题 -2024-05-14 v2.7.4 - -1. 修复通过抖音号进入直播偶见获取不到Owner对象和WebRoomid的问题; -2. 修复部分电脑启动提示 ‘字符串格式错误’ 导致无法启动的问题 -3. 2024.05.16 补丁: 修复在使用窗体的情况下,因跨线程调用抛出异常导致的弹幕断连问题 - -2024-04-26 v2.7.3 (需要升级版本,请先仔细阅读) - -1. 支持串口转发,详见下方说明**串口转发** -2. 推送包字段更新
- 1. 推送包添加 `Onwer`字段,用户描述主播基本信息,客户端应优先使用其中的`SecUid`或者`Uid`作为直播间标识 - 2. **[破坏性更新]** `WebRoomid` 和 `Roomid` 改为String类型 - 3. 之前直播伴侣开播会导致`WebRoomid`为-1,现会抓取直播伴侣开播响应信息,并填充`WebRoomid`,但直播伴侣的`WebRoomid`设定为主播**抖音号**,需要根据进程名做判断,使用请注意 - -2024-03-10 v2.7.2 - -1. [**重要更新**] 支持设置为轮询模式监听(见配置项**forcePolling**,直播伴侣尚未支持),比起ws更稳定,适合长时间监听 -2. 支持进入直播间画面自动暂停 -3. 优化礼物连击判定规则,可区分出可连击的礼物,进一步避免礼物数量推送不准确的问题,并新增字段: - 1. Combo 礼物是否可连击 - 2. ImgUrl 礼物图像地址 - 3. ToUser 送礼目标 -4. roomId过滤改为 WebRoomid 过滤(仅支持已经被缓存的直播间,对直播伴侣可能无效) -5. 添加“关闭代理.bat” 批处理脚本,见根目录或者上方文件,便于无法上网时双击执行 -6. 更多介绍请查看底下新增配置项 - -2023-11-23 v2.7.0 - -1. 修复了因抖音版本升级导致的WebRoomId获取不到的问题,并更近了正则表达式尽量兼容后续的变更 -2. 添加了winfrom窗体,功能有限,默认隐藏 -3. 支持了控制台隐藏,推送器弹幕类型过滤,日志弹幕类型过滤,弹幕文件日志 (见配置文件) -4. 支持了更多的ws命令,见 [Command.cs](./BarrageGrab/Modles/JsonEntity/Command.cs) ## 🖼️控制台截图 @@ -88,6 +59,7 @@ ### 介绍 + 基于系统代理抓包打造的抖音弹幕服务推送程序,它能够获取电脑上所有抖音弹幕来源数据,主要包括三种来源:**浏览器进程** ,**抖音客户端**,**抖音直播伴侣**。它可以监听**弹幕**,**点赞**,**关注**,**送礼**,**进入直播间**,**直播间统计**,**粉丝团**系列消息,你可使用它做自己的直播间数据分析,以及弹幕互动游戏,语音播报等。 + + 程序含WebSocket服务,若需要对接自己的应用,或者扩展功能,可以进行接入 ### 配置文件 @@ -95,71 +67,73 @@ + 若下载发行版,则该配置文件在根目录下的 **WssBarrageService.exe.config** 可自主配置,配置后需要重新启动程序 ``` xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ``` ### 使用方法 1. 管理员身份启动本程序,第一次启动会提示安装自签名证书,程序启动后挂在后台不要关,程序无法中途监听已经建立连接的弹幕流 + 2. 打开浏览器进入任何直播间,然后观察程序控制台中是否有弹幕滚动 + 3. 如果有请往下看,否则请参考诊断排查 + 4. 启动后程序控制台标题会显示 ws连接地址,如果需要对接自己的应用程序,请先了解 [WebSocket](https://www.bilibili.com/video/BV1KN411n7WD/?share_source=copy_web&vd_source=39ebf42c923024b9eddeca32c9e46d16) ,然后使用自己的程序进行对接,可前往[在线测试网站](http://wstool.jackxiang.com/)进行连接测试 + 5. 对于字段不理解,请参考 [实体类源码文件](./BarrageGrab/Modles/JsonEntity/BarrageMessages.cs) 的注释,或者加群询问 - + ## 🔌 串口转发 + 配置串口:见配置文件`comPort`,配置发送串口以及波特率,不填则不启用 -+ 串口报文自定义:可在根目录`Scripts/engine/comPortFilter.js` 根据自己的需求配置转发报文,提供了一定的Bit转换函数,详见其注释 ++ 串口报文自定义:配置串口后,程序会自动生成配置模板文件 `scripts/comPortFilter.js`, 可根据自己的需求配置转发报文,提供了一定的Bit转换函数,详见其注释 ## 👨‍⚕️ 诊断排查 @@ -182,9 +156,6 @@ + *尝试执行未经授权的操作* + 关杀毒 - + *以上都不行* - + 请前往[这里](https://www.baidu.com/s?word=新款电脑淘宝)瞧瞧 - + **弹幕断连** + 如果浏览器总是连接一段时间后,就在在控制台看不到弹幕了,优先使用Chrome 和 Edge 浏览器,其他浏览器(例如360浏览器) 可能会存在该问题 diff --git a/imgs/logo.ico b/imgs/logo.ico new file mode 100644 index 0000000..ac8c450 Binary files /dev/null and b/imgs/logo.ico differ