diff --git a/BarrageGrab/App.config b/BarrageGrab/App.config index d3c6973..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/AppSetting.cs b/BarrageGrab/AppSetting.cs index ab456c9..fb2437a 100644 --- a/BarrageGrab/AppSetting.cs +++ b/BarrageGrab/AppSetting.cs @@ -2,10 +2,12 @@ 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; namespace BarrageGrab @@ -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/JsEngine.cs b/BarrageGrab/JsEngine.cs index caa955a..0ad2fd9 100644 --- a/BarrageGrab/JsEngine.cs +++ b/BarrageGrab/JsEngine.cs @@ -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)) return null; - 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 diff --git a/BarrageGrab/Properties/AssemblyInfo.cs b/BarrageGrab/Properties/AssemblyInfo.cs index e0669c2..87fe95c 100644 --- a/BarrageGrab/Properties/AssemblyInfo.cs +++ b/BarrageGrab/Properties/AssemblyInfo.cs @@ -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.8")] -[assembly: AssemblyFileVersion("2.7.8")] +[assembly: AssemblyVersion("2.7.9")] +[assembly: AssemblyFileVersion("2.7.9")] [assembly: NeutralResourcesLanguage("zh-CN")] diff --git a/BarrageGrab/Server/ComPortServer.cs b/BarrageGrab/Server/ComPortServer.cs index bf9ea2a..9ff1c16 100644 --- a/BarrageGrab/Server/ComPortServer.cs +++ b/BarrageGrab/Server/ComPortServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.IO.Ports; using System.Linq; using System.Text; @@ -22,26 +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"); - if (jsFile.IsNullOrWhiteSpace()) - { - jsFile = EmbResource.GetFileContent("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) @@ -70,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); @@ -152,7 +157,7 @@ namespace BarrageGrab sendPort.Close(); sendPort.Dispose(); sendPort = null; - throw ex; + Logger.LogError(ex, $"串口打开失败, {ex.Message}"); } } diff --git a/BarrageGrab/WssBarrageService.csproj b/BarrageGrab/WssBarrageService.csproj index 734a80d..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 @@ -259,14 +262,16 @@ - + WssBarrageService + + 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