diff --git a/BarrageGrab/App.config b/BarrageGrab/App.config
index e119cab..91a6d61 100644
--- a/BarrageGrab/App.config
+++ b/BarrageGrab/App.config
@@ -10,6 +10,8 @@
+
+
@@ -22,8 +24,6 @@
-
-
@@ -41,11 +41,13 @@
-
+
+
+
@@ -53,6 +55,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BarrageGrab/AppSetting.cs b/BarrageGrab/AppSetting.cs
index 5a08010..188c0bf 100644
--- a/BarrageGrab/AppSetting.cs
+++ b/BarrageGrab/AppSetting.cs
@@ -40,6 +40,7 @@ namespace BarrageGrab
PollingInterval = int.Parse(AppSettings["pollingInterval"].Trim());
DisableLivePageScriptCache = bool.Parse(AppSettings["disableLivePageScriptCache"].Trim());
WebRoomIds = AppSettings["webRoomIds"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();
+ LiveCompanPath = AppSettings["liveCompanPath"].Trim();
ConfigComPort();
ConfigFilter();
@@ -243,5 +244,10 @@ namespace BarrageGrab
/// 是否启用串口过滤器脚本
///
public bool UseComPortFilter { get; set; }
+
+ ///
+ /// 直播伴侣文件位置
+ ///
+ public string LiveCompanPath { get; set; } = string.Empty;
}
}
diff --git a/BarrageGrab/FodyWeavers.xml b/BarrageGrab/FodyWeavers.xml
new file mode 100644
index 0000000..a5dcf04
--- /dev/null
+++ b/BarrageGrab/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/BarrageGrab/LiveCompanHelper.cs b/BarrageGrab/LiveCompanHelper.cs
new file mode 100644
index 0000000..7785ce0
--- /dev/null
+++ b/BarrageGrab/LiveCompanHelper.cs
@@ -0,0 +1,173 @@
+using HtmlAgilityPack;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+using IWshRuntimeLibrary;
+using File = System.IO.File;
+
+namespace BarrageGrab
+{
+ public static class LiveCompanHelper
+ {
+ ///
+ /// 获取抖音直播伴侣的exe路径
+ ///
+ ///
+ public static string GetExePath()
+ {
+ string exePath = "";
+ //获取exe所在目录
+ //1.先在开始菜单找
+ var shortcutPath = @"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\直播伴侣.lnk";
+ if (File.Exists(shortcutPath))
+ {
+ //获取快捷方式的目标路径
+ exePath = GetInkTargetPath(shortcutPath);
+ if (!File.Exists(exePath))
+ {
+ exePath = "";
+ Logger.LogWarn($"直播伴侣.lnk 所指向的exe文件位置 {exePath} 无效");
+ }
+ }
+ else
+ {
+ Logger.LogInfo("未找到直播伴侣的桌面快捷方式");
+ }
+
+ //再从配置文件读取
+ if (exePath.IsNullOrWhiteSpace())
+ {
+ exePath = AppSetting.Current.LiveCompanPath;
+ if(!exePath.IsNullOrWhiteSpace() && !File.Exists(exePath))
+ {
+ Logger.LogWarn($"所配置的 {exePath} 不存在");
+ exePath = "";
+ }
+ }
+ return exePath;
+ }
+
+ ///
+ /// 初始化直播伴侣环境
+ ///
+ public static void SwitchSetup()
+ {
+ var exePath = GetExePath();
+ if (string.IsNullOrEmpty(exePath))
+ {
+ Logger.LogWarn("未找到直播伴侣.exe文件,跳过环境设置");
+ return;
+ }
+
+ //设置index.js
+ var indexJsPath = Path.Combine(Path.GetDirectoryName(exePath), "resources", "app", "index.js");
+ Logger.LogInfo($"正在配置 " + indexJsPath);
+
+ if (!File.Exists(indexJsPath))
+ {
+ throw new Exception("未找到直播伴侣的index.js文件");
+ }
+ var indexJs = File.ReadAllText(indexJsPath, Encoding.UTF8);
+
+ CheckBackFile(indexJsPath);
+ var newjs = SetIndexJsContent(indexJs);
+ if (newjs != indexJs && !newjs.IsNullOrWhiteSpace())
+ {
+ File.WriteAllText(indexJsPath, newjs);
+ }
+
+ Logger.LogInfo("直播伴侣环境初始化完成");
+ }
+
+ //获取Ink快捷方式的目标路径
+ private static string GetInkTargetPath(string shortcutPath)
+ {
+ // 创建 Windows Script Host 对象
+ WshShell shell = new WshShell();
+
+ // 使用 IWshShortcut 接口加载 .ink 文件
+ IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutPath);
+
+ // 获取目标路径
+ string targetPath = shortcut.TargetPath;
+
+ return targetPath;
+ }
+
+ //检测备份文件
+ private static void CheckBackFile(string filePath)
+ {
+ if (!File.Exists(filePath)) return;
+
+ var bakPath = filePath + ".bak";
+ if (!File.Exists(bakPath))
+ {
+ //拷贝一份备份
+ File.Copy(filePath, bakPath);
+ Logger.LogInfo($"已备份 {filePath} -> {bakPath}");
+ }
+ }
+
+ //设置index.js内容
+ private static string SetIndexJsContent(string content)
+ {
+ //检测注入代理
+ var mineProxyHost = $"127.0.0.1:{AppSetting.Current.ProxyPort},direct://";
+ SetSwitch("proxy-server", mineProxyHost, ref content);
+
+ //移除文件损坏检测校验
+ var checkReg = new Regex(@"if\(\(0,\w.integrityCheckReport\)\(\w\),!\w\.ok\)");
+ if (checkReg.IsMatch(content))
+ {
+ content = checkReg.Replace(content, "if(false)");
+ Logger.LogInfo($"直播伴侣文件改动检测已拦截");
+ }
+
+ return content;
+ }
+
+ //添加 electron 启动参数
+ private static void SetSwitch(string name, string value, ref string content)
+ {
+ if (name.IsNullOrWhiteSpace()) return;
+
+ //检测注入
+ var proxyReg = new Regex($@"(?\w+)\.commandLine\.appendSwitch\(""{name}"",""(?[^""]*)""\)");
+ var proxyMatch = proxyReg.Match(content);
+ //检测到已经存在配置,则更新参数值
+ if (proxyMatch.Success)
+ {
+ var matchValue = proxyMatch.Groups["value"].Value;
+ if (value != matchValue)
+ {
+ content = proxyReg.Replace(content, $@"${{varname}}.commandLine.appendSwitch(""{name}"",""{value}"")");
+ Logger.LogInfo($"直播伴侣成功覆盖启动参数 [{name}] = [{value}]");
+ }
+ }
+ //否则添加新的配置
+ else
+ {
+ var nosandboxReg = new Regex(@"(?\w+)\.commandLine\.appendSwitch\(""no-sandbox""\)");
+ var match = nosandboxReg.Match(content);
+ if (match.Success)
+ {
+ var newvalue = $@"{match.Groups["varname"].ToString()}.commandLine.appendSwitch(""{name}"",""{value}""),";
+ content = content.Insert(match.Index, newvalue);
+ Logger.LogInfo($"直播伴侣成功添加启动参数 [{name}] = [{value}]");
+ }
+ }
+ }
+
+ private static void SetInnerText(HtmlNode node, string text)
+ {
+ node.InnerHtml = "";
+ var textNode = node.OwnerDocument.CreateTextNode(text);
+ node.AppendChild(textNode);
+ }
+ }
+}
diff --git a/BarrageGrab/ObjectExtension.cs b/BarrageGrab/ObjectExtension.cs
index 3b5b7a7..916578d 100644
--- a/BarrageGrab/ObjectExtension.cs
+++ b/BarrageGrab/ObjectExtension.cs
@@ -91,7 +91,28 @@ namespace BarrageGrab
foreach (var item in list) func(item, i++);
}
-
+ ///
+ /// 是否包含
+ ///
+ ///
+ ///
+ ///
+ public static bool In(this T obj, params T[] args)
+ {
+ return args.Contains(obj);
+ }
+
+ ///
+ /// 是否包含
+ ///
+ ///
+ ///
+ ///
+ public static bool LikeIn(this string str, params string[] args)
+ {
+ return args.Any(a => str.Contains(a));
+ }
+
public static CookieCollection ToCookies(this string cookieStr, string domain)
{
var cookies = new CookieCollection();
@@ -170,6 +191,133 @@ namespace BarrageGrab
if (cookies == null) return;
var cookieCollection = dic.ToCookies(domain);
cookies.Add(cookieCollection);
- }
+ }
+
+ ///
+ /// 将字典转为查询参数
+ ///
+ ///
+ ///
+ public static string ToQueryParam(this IDictionary dic, bool encode = true)
+ {
+ if (dic == null || dic.Count == 0)
+ return string.Empty;
+
+ var queryParams = dic.Select(kvp => kvp.Key + "=" + (encode ? WebUtility.UrlEncode(kvp.Value) : kvp.Value));
+ return string.Join("&", queryParams);
+
+ }
+
+ ///
+ /// 赋值字典,没有则添加,有则覆盖
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Set(this IDictionary dic, TKey key, TValue value)
+ {
+ if (dic.ContainsKey(key))
+ {
+ dic[key] = value;
+ }
+ else
+ {
+ dic.Add(key, value);
+ }
+ }
+
+ ///
+ /// 赋值字典,没有则添加,有则覆盖
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Set(this IDictionary dic, KeyValuePair kv)
+ {
+ dic.Set(kv.Key, kv.Value);
+ }
+
+ ///
+ /// 获取Uri查询参数
+ ///
+ ///
+ ///
+ ///
+ public static IDictionary GetQueryParams(this UriBuilder uri)
+ {
+ if (uri == null)
+ {
+ throw new ArgumentNullException(nameof(uri));
+ }
+
+ var queryParams = uri.Query.TrimStart('?');
+
+ if (string.IsNullOrEmpty(queryParams))
+ {
+ return new Dictionary();
+ }
+
+ return queryParams
+ .Split('&')
+ .Select(param => param.Split('='))
+ .ToDictionary(
+ keyValuePair => Uri.UnescapeDataString(keyValuePair[0]),
+ keyValuePair => Uri.UnescapeDataString(keyValuePair.Length > 1 ? keyValuePair[1] : ""));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void AddQueryParam(this UriBuilder uri, IEnumerable> vals)
+ {
+ if (uri == null) return;
+ var dic = uri.GetQueryParams();
+ foreach (var item in vals)
+ {
+ dic.Set(item);
+ }
+ uri.Query = dic.ToQueryParam();
+ }
+
+ ///
+ /// 删除一个query项
+ ///
+ ///
+ ///
+ public static void RemoveQueryParam(this UriBuilder uri, string key)
+ {
+ if (uri == null) return;
+ var dic = uri.GetQueryParams();
+ dic.Remove(key);
+ if (dic.Count > 0)
+ {
+ uri.Query = "?" + dic.ToQueryParam();
+ }
+ else
+ {
+ uri.Query = "";
+ }
+ }
+
+ ///
+ /// 添加参数
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void AddQueryParam(this UriBuilder uri, string key, string value)
+ {
+ if (uri == null) return;
+ uri.AddQueryParam(new[] {
+ new KeyValuePair(key,value)
+ });
+ }
+
}
}
diff --git a/BarrageGrab/Program.cs b/BarrageGrab/Program.cs
index 1e9d92c..8752188 100644
--- a/BarrageGrab/Program.cs
+++ b/BarrageGrab/Program.cs
@@ -18,19 +18,16 @@ namespace BarrageGrab
static bool exited = false;
static bool formExited = false;
static WinApi.ControlCtrlDelegate controlCtr = ControlCtrlHandle;
-
+ static Mutex mutex = new Mutex(false, "DyBarrageGrab");
static void Main(string[] args)
{
- //JintTest();
- //Console.WriteLine("结束");
- //Console.ReadKey();
- //return;
- if (CheckAlreadyRunning())
+ if (!mutex.WaitOne(TimeSpan.Zero, true))
{
- Logger.PrintColor("已经有一个监听程序在运行,按任意键退出...");
+ Console.WriteLine("另一个实例已在运行。");
Console.ReadKey();
return;
}
+
SetTitle("抖音弹幕监听推送");
try
@@ -39,7 +36,7 @@ namespace BarrageGrab
}
catch (Exception ex)
{
- Logger.LogError(ex, "程序初始化错误");
+ Logger.LogError(ex, $"程序初始化错误,{ex.Message}");
MessageBox.Show(ex.Message, "程序初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
exited = true;
}
@@ -56,12 +53,12 @@ namespace BarrageGrab
Logger.PrintColor("服务器已关闭...");
WinApi.SetConsoleCtrlHandler(controlCtr, false);//反注册捕获控制台关闭
-
}
private static void Init()
{
AppRuntime.Init();
+ LiveCompanHelper.SwitchSetup();
WinApi.SetConsoleCtrlHandler(controlCtr, true);//捕获控制台关闭
WinApi.DisableQuickEditMode();//禁用控制台快速编辑模式
AppRuntime.DisplayConsole(!AppSetting.Current.HideConsole);//控制控制台可见
@@ -94,8 +91,10 @@ namespace BarrageGrab
}
AppRuntime.WsServer.StartListen();//启动WS以及代理服务
- Logger.PrintColor($"{AppRuntime.WsServer.ServerLocation} 弹幕服务已启动...",ConsoleColor.Green);
- SetTitle($"抖音弹幕监听推送 [{AppRuntime.WsServer.ServerLocation}]");
+ Logger.PrintColor($"{AppRuntime.WsServer.ServerLocation} 弹幕服务已启动,其他端可通过此地址获取到弹幕流信息", ConsoleColor.Green);
+
+ Version version = System.Reflection.Assembly.GetAssembly(typeof(Program)).GetName().Version;
+ SetTitle($"抖音弹幕监听推送 v{version} [{AppRuntime.WsServer.ServerLocation}]");
}
//检测设置控制台标题
@@ -123,53 +122,5 @@ namespace BarrageGrab
}
return false;
}
-
- //检测程序是否多开
- private static bool CheckAlreadyRunning()
- {
- const string mutexName = "DyBarrageGrab";
- // Try to create a new named mutex.
- bool createdNew;
- using (Mutex mutex = new Mutex(true, mutexName, out createdNew))
- {
- return !createdNew;
- }
- }
-#if DEBUG
- private static void JintTest()
- {
- var jsEng = JsEngine.CreateNewEngine();
- var jsFile = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Scripts", "engine", "comPortFilter.js"), Encoding.UTF8);
- //var jsFile = @"D:\develop\Project\dy-barrage-grab\BarrageGrab\Scripts\engine\comPortFilter.js";
- jsEng.Execute(jsFile);
- var result = jsEng.Invoke("onPackData", 1, new
- {
- Aporp = 142,
- Bprop = "48777",
- Cprop = new string[] { "123" },
- Dprop = new Dictionary()
- {
- { "key1","value1" }
- }
- },null);
- Console.WriteLine($"返回类型:{ result.Type.ToString()},值:{result.ToString()}");
- if (result.Type == Types.String)
- {
- Console.WriteLine("返回String: " + result.ToString());
- }
- if (result.Type == Types.Object)
- {
- var obj = result.ToObject();
- if (obj is byte[])
- {
- Console.WriteLine("返回byte[]");
- }
- }
- if (result.Type == Types.Symbol)
- {
- Console.WriteLine("返回Symbol: " + result.ToString());
- }
- }
-#endif
}
}
diff --git a/BarrageGrab/Properties/AssemblyInfo.cs b/BarrageGrab/Properties/AssemblyInfo.cs
index a36c1ed..4b295b0 100644
--- a/BarrageGrab/Properties/AssemblyInfo.cs
+++ b/BarrageGrab/Properties/AssemblyInfo.cs
@@ -33,6 +33,6 @@ using System.Runtime.InteropServices;
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.7.4.0")]
-[assembly: AssemblyFileVersion("2.7.4.0")]
+[assembly: AssemblyVersion("2.7.6.0")]
+[assembly: AssemblyFileVersion("2.7.6.0")]
[assembly: NeutralResourcesLanguage("zh-CN")]
diff --git a/BarrageGrab/Proxy/TitaniumProxy.cs b/BarrageGrab/Proxy/TitaniumProxy.cs
index b202bdc..404190d 100644
--- a/BarrageGrab/Proxy/TitaniumProxy.cs
+++ b/BarrageGrab/Proxy/TitaniumProxy.cs
@@ -16,6 +16,13 @@ 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;
namespace BarrageGrab.Proxy
{
@@ -65,7 +72,7 @@ namespace BarrageGrab.Proxy
Logger.LogWarn("代理环境设置失败:" + ex.Message);
}
}
-
+
public TitaniumProxy()
{
//注册系统代理
@@ -75,9 +82,10 @@ namespace BarrageGrab.Proxy
proxyServer.ReuseSocket = false;
proxyServer.EnableConnectionPool = true;
proxyServer.ForwardToUpstreamGateway = true;
- proxyServer.CertificateManager.SaveFakeCertificates = true;
- proxyServer.CertificateManager.RootCertificate = proxyServer.CertificateManager.LoadRootCertificate();
+ proxyServer.CertificateManager.CertificateValidDays = 365 * 10;
+ proxyServer.CertificateManager.SaveFakeCertificates = true;
+ proxyServer.CertificateManager.RootCertificate = GetCert();
if (proxyServer.CertificateManager.RootCertificate == null)
{
Logger.PrintColor("正在进行证书安装,需要信任该证书才可进行https解密,若有提示请确定");
@@ -97,6 +105,64 @@ namespace BarrageGrab.Proxy
proxyServer.AddEndPoint(explicitEndPoint);
}
+ private X509Certificate2 GetCert()
+ {
+ X509Certificate2 result = proxyServer.CertificateManager.LoadRootCertificate();
+
+ if (result != null) return result;
+
+ return null;
+
+ // 打开“受信任的根证书颁发机构”存储区
+ using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
+ {
+ store.Open(OpenFlags.ReadOnly);
+
+ // 遍历证书集合
+ foreach (X509Certificate2 cert in store.Certificates)
+ {
+ try
+ {
+ // 尝试使用证书创建 X509Certificate2 实例
+ X509Certificate2 certificate = new X509Certificate2(cert);
+
+ //判断过期
+ if (DateTime.Now > certificate.NotAfter) continue;
+
+ //判断黑名单
+ var black = new string[] { "localhost" };
+ if (certificate.FriendlyName.ToLower().LikeIn(black) ||
+ certificate.Subject.ToLower().LikeIn(black)
+ ) continue;
+
+
+ if (certificate.FriendlyName.ToLower().LikeIn("titanium"))
+ {
+ result = certificate;
+ break;
+ }
+
+ // 打印证书信息
+ //Console.WriteLine("证书: " + certificate.Subject);
+ //Console.WriteLine("颁发者: " + certificate.Issuer);
+ //Console.WriteLine("有效期: " + certificate.NotBefore + " - " + certificate.NotAfter);
+
+ //result = certificate;
+ //break;
+ }
+ catch (CryptographicException ex)
+ {
+ //捕获加密异常,如果证书需要密码,这里会抛出异常
+ throw new Exception("证书加载失败: " + ex.Message);
+ }
+ }
+
+ store.Close();
+ }
+
+ return result;
+ }
+
public override void SetUpstreamProxy(string upstreamProxyAddr)
{
if (string.IsNullOrWhiteSpace(upstreamProxyAddr)) return;
@@ -121,6 +187,11 @@ namespace BarrageGrab.Proxy
}
}
+ private bool CheckBrowser(string processName)
+ {
+ return AppSetting.Current.ProcessFilter.Contains(processName) && processName!="直播伴侣" && processName!= "douyin";
+ }
+
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{
string uri = e.HttpClient.Request.RequestUri.ToString();
@@ -208,7 +279,7 @@ namespace BarrageGrab.Proxy
if (uri.Contains(BARRAGE_POOL_PATH) && contentType.Contains("application/protobuffer"))
{
var payload = await e.GetResponseBody();
-
+
base.FireOnFetchResponse(new HttpResponseEventArgs()
{
HttpClient = e.HttpClient,
@@ -274,7 +345,7 @@ namespace BarrageGrab.Proxy
});
liveRoomInjectScript = scriptContext + liveRoomInjectScript;
var html = await e.GetResponseBodyAsString();
- var doc = new HtmlAgilityPack.HtmlDocument();
+ var doc = new HtmlDocument();
doc.LoadHtml(html);
if (!liveRoomInjectScript.IsNullOrWhiteSpace())
@@ -311,7 +382,6 @@ namespace BarrageGrab.Proxy
AppRuntime.RoomCaches.AddRoomInfoCache(roominfo);
}
-
try
{
@@ -335,27 +405,11 @@ namespace BarrageGrab.Proxy
//给script标签 src加上时间戳避免缓存
if (AppSetting.Current.DisableLivePageScriptCache)
{
- var scripts = doc.DocumentNode.SelectNodes("//script[@src]");
- if (scripts != null)
- {
- var ticks = DateTime.Now.Ticks;
- foreach (var script in scripts)
- {
- var src = script.Attributes["src"].Value;
- if (src.Contains("?"))
- {
- src += "&_t=" + ticks;
- }
- else
- {
- src += "?_t=" + ticks;
- }
- script.Attributes["src"].Value = src;
- }
- html = doc.DocumentNode.OuterHtml;
- }
+ ScriptAddTocks(doc);
}
+ html = doc.DocumentNode.OuterHtml;
+
e.SetResponseBodyString(html);
}
@@ -395,6 +449,38 @@ namespace BarrageGrab.Proxy
}
}
+ //给部分脚本加上时间戳避免缓存
+ private void ScriptAddTocks(HtmlDocument doc)
+ {
+ var scripts = doc.DocumentNode.SelectNodes("//script[@src]");
+ if (scripts != null)
+ {
+ var ticks = DateTime.Now.Ticks;
+ foreach (var script in scripts)
+ {
+ var src = script.Attributes["src"].Value;
+
+ var srcUri = new Uri(src);
+ if (!CheckHost(srcUri.Host)) continue;
+
+ var fileName = Path.GetFileName(src.Split('?')[0]);
+ //目前只需要用到相关这些js
+ if (!fileName.StartsWith("island") && !src.Contains(LIVE_SCRIPT_PATH)) continue;
+
+ if (src.Contains("?"))
+ {
+ src += "&_t=" + ticks;
+ }
+ else
+ {
+ src += "?_t=" + ticks;
+ }
+ script.Attributes["src"].Value = src;
+ }
+
+ }
+ }
+
//生成注入上下文
private string BuildContext(IDictionary constVals)
{
@@ -415,6 +501,7 @@ namespace BarrageGrab.Proxy
var processid = e.HttpClient.ProcessId.Value;
var processName = base.GetProcessName(processid);
var contentType = e.HttpClient.Response.ContentType ?? "";
+ var fileName = Path.GetFileName(urlNoQuery);
if (contentType == null) return;
if (!contentType.Trim().ToLower().Contains("application/javascript")) return;
@@ -424,19 +511,17 @@ namespace BarrageGrab.Proxy
if (hostname == SCRIPT_HOST
//这俩个进程不需要注入
&& processName != "直播伴侣" && processName != "douyin"
+ && fileName.StartsWith("island")
)
{
var js = await e.GetResponseBodyAsString();
- //var reg2 = new Regex(@"if\(!N.DJ\(\)&&(?\S).current\)\{"); //版本1,已过时
- //if(!(0,k.DJ)()&&_.current){
-
- var reg2 = new Regex(@"if\s*\(\s*\!\s*(?[\s\S]+\s*\.\s*DJ\s*\))\s*\(\s*\)\s*&\s*&\s*(?\S)\s*.\s*current\s*\)\s*\{");
+ var reg2 = new Regex(@"if\(!\((?\d,\w{1,2})\.DJ\)\(\)&&");
var match = reg2.Match(js);
if (match.Success)
{
- js = reg2.Replace(js, "if(!${v1}()&&${v2}.current){return;");
+ js = reg2.Replace(js, "if(false &&");
e.SetResponseBodyString(js);
- Logger.PrintColor($"已成功绕过JS页面无操作检测 {urlNoQuery}\n", ConsoleColor.Green);
+ Logger.PrintColor($"已成功绕过页面无操作检测\n", ConsoleColor.Green);
}
}
@@ -479,6 +564,12 @@ namespace BarrageGrab.Proxy
string url = e.HttpClient.Request.RequestUri.ToString();
string hostname = e.HttpClient.Request.RequestUri.Host;
+ e.DecryptSsl = CheckHost(hostname);
+ }
+
+ //检测域名白名单
+ protected override bool CheckHost(string hostname)
+ {
//需要解析SSL的域名 放在这里,全开会导致性能问题,应只解析业务需要的域名
var decryptSsls = new string[]
{
@@ -489,24 +580,11 @@ namespace BarrageGrab.Proxy
if (decryptSsls.Contains(hostname))
{
- e.DecryptSsl = true;
- return;
+ return true;
}
- else
- {
- if (!CheckHost(hostname))
- {
- e.DecryptSsl = false;
- }
- }
- }
- //检测域名白名单
- protected override bool CheckHost(string host)
- {
- host = host.Trim().ToLower();
- var succ = base.CheckHost(host);
- return succ || host == SCRIPT_HOST || host == LIVE_HOST;
+ hostname = hostname.Trim().ToLower();
+ return base.CheckHost(hostname);
}
//WebSocket 流读取
@@ -585,14 +663,20 @@ namespace BarrageGrab.Proxy
///
override public void Start()
{
- proxyServer.Start(AppSetting.Current.UsedProxy);
+ proxyServer.Start(false);
+
if (AppSetting.Current.UsedProxy)
{
base.RegisterSystemProxy();
+ Logger.LogInfo($"系统代理代理已启动,127.0.0.1:{base.ProxyPort}");
//使用其自带的系统代理设置可能会导致格式问题
//proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
//proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
}
+ else
+ {
+ Logger.LogInfo($"代理已启动(局域代理),127.0.0.1:{base.ProxyPort}");
+ }
}
}
}
diff --git a/BarrageGrab/WsBarrageServer.cs b/BarrageGrab/WsBarrageServer.cs
index 098dca1..eabb0c2 100644
--- a/BarrageGrab/WsBarrageServer.cs
+++ b/BarrageGrab/WsBarrageServer.cs
@@ -201,7 +201,7 @@ namespace BarrageGrab
private void PrintMsg(Msg msg, PackMsgType barType)
{
var rinfo = AppRuntime.RoomCaches.GetCachedWebRoomInfo(msg.RoomId.ToString());
- var roomName = (rinfo?.Owner?.Nickname ?? ("直播间" + (msg.WebRoomId.IsNullOrWhiteSpace() ? msg.RoomId.ToString() : msg.WebRoomId)));
+ var roomName = (rinfo?.Owner?.Nickname ?? ((msg.WebRoomId.IsNullOrWhiteSpace() ? msg.RoomId.ToString() : msg.WebRoomId)));
var text = $"{DateTime.Now.ToString("HH:mm:ss")} [{roomName}] [{barType}]";
if (msg.User != null)
@@ -347,48 +347,41 @@ namespace BarrageGrab
ogift.Name = findForData.name;
}
-
-
- //Combo 为1时,表示为可连击礼物
- //if (msg.Gift.Combo) //一堆礼物Combo 和实际可连击情况不匹配,例如亲吻,闪耀星辰 Combo =false,实际上是可连击的,抖音那帮程序员怕是看女主播冲昏头了
- if (true)
+ //判断礼物重复
+ if (msg.repeatEnd == 1 && giftCountCache.ContainsKey(key))
{
- //判断礼物重复
- if (msg.repeatEnd == 1)
+ //清除缓存中的key
+ if (msg.groupId > 0 )
{
- //清除缓存中的key
- if (msg.groupId > 0 && giftCountCache.ContainsKey(key))
- {
- Tuple _;
- giftCountCache.TryRemove(key, out _);
- }
- return;
+ Tuple _;
+ giftCountCache.TryRemove(key, out _);
}
- var backward = currCount <= lastCount;
- if (currCount <= 0) currCount = 1;
-
- if (giftCountCache.ContainsKey(key))
- {
- lastCount = giftCountCache[key].Item1;
- backward = currCount <= lastCount;
- if (!backward)
- {
- lock (giftCountCache)
- {
- giftCountCache[key] = Tuple.Create(currCount, DateTime.Now);
- }
- }
- }
- else
- {
- if (msg.groupId > 0 && !backward)
- {
- giftCountCache.TryAdd(key, Tuple.Create(currCount, DateTime.Now));
- }
- }
- //比上次小,则说明先后顺序出了问题,直接丢掉,应为比它大的消息已经处理过了
- if (backward) return;
+ return;
}
+ var backward = currCount <= lastCount;
+ if (currCount <= 0) currCount = 1;
+
+ if (giftCountCache.ContainsKey(key))
+ {
+ lastCount = giftCountCache[key].Item1;
+ backward = currCount <= lastCount;
+ if (!backward)
+ {
+ lock (giftCountCache)
+ {
+ giftCountCache[key] = Tuple.Create(currCount, DateTime.Now);
+ }
+ }
+ }
+ else
+ {
+ if (msg.groupId > 0 && !backward)
+ {
+ giftCountCache.TryAdd(key, Tuple.Create(currCount, DateTime.Now));
+ }
+ }
+ //比上次小,则说明先后顺序出了问题,直接丢掉,应为比它大的消息已经处理过了
+ if (backward) return;
var count = currCount - lastCount;
diff --git a/BarrageGrab/WssBarrageService.csproj b/BarrageGrab/WssBarrageService.csproj
index 17ed360..e3e1145 100644
--- a/BarrageGrab/WssBarrageService.csproj
+++ b/BarrageGrab/WssBarrageService.csproj
@@ -1,5 +1,6 @@
+
false
@@ -29,6 +30,8 @@
1.0.0.%2a
false
true
+
+
true
@@ -70,6 +73,9 @@
..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll
+
+ ..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll
+
..\packages\Esprima.3.0.4\lib\net462\Esprima.dll
True
@@ -84,6 +90,9 @@
..\packages\Jint.3.0.1\lib\net462\Jint.dll
True
+
+ ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
+
..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll
@@ -100,29 +109,93 @@
..\packages\protobuf-net.Core.3.1.22\lib\net462\protobuf-net.Core.dll
+
+ ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll
+
..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll
+
+
+ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
+
+
+ ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
+
+
+ ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll
+
-
+
+ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
+
+
+ ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
+
+
+ ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
+
+
+
+ ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
+
+
+ ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
+
+
+ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
+
..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll
+
+ ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
+
+
+ ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
+
..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+ ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll
+
+
+ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
+
..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
+
+
+ ..\packages\System.Runtime.InteropServices.4.3.0\lib\net462\System.Runtime.InteropServices.dll
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+
..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+
..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
@@ -135,8 +208,10 @@
-
+
+ ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
+
..\packages\Titanium.Web.Proxy.3.2.2-beta\lib\net461\Titanium.Web.Proxy.dll
@@ -160,6 +235,7 @@
RoomDetail.cs
+
@@ -194,6 +270,7 @@
+
PreserveNewest
@@ -221,6 +298,26 @@
RoomDetail.cs
-
+
+
+ {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}
+ 1
+ 0
+ 0
+ tlbimp
+ False
+ True
+
+
+
+
+ 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BarrageGrab/packages.config b/BarrageGrab/packages.config
index e0afd29..8fad1c7 100644
--- a/BarrageGrab/packages.config
+++ b/BarrageGrab/packages.config
@@ -2,23 +2,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Demos/Python/LogWindow.py b/Demos/Python/LogWindow.py
new file mode 100644
index 0000000..22e911d
--- /dev/null
+++ b/Demos/Python/LogWindow.py
@@ -0,0 +1,291 @@
+# ruff: noqa: F401,F403,F405,E402,F541,E722
+import asyncio
+import websockets
+import json
+import os
+import ctypes
+import sys
+import time
+import winreg
+import tkinter as tk
+from tkinter import ttk
+import customtkinter
+import subprocess
+import random
+import threading
+import atexit
+import psutil
+import queue
+
+# 创建一个队列
+gui_queue = queue.Queue()
+
+# 创建一个线程安全的标志
+stop_flag = threading.Event()
+
+
+def is_admin():
+ try:
+ return ctypes.windll.shell32.IsUserAnAdmin()
+ except:
+ return False
+
+
+def disable_proxy():
+ internet_settings = winreg.OpenKey(
+ winreg.HKEY_CURRENT_USER,
+ r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
+ 0,
+ winreg.KEY_ALL_ACCESS,
+ )
+
+ # 设置代理使能为0(禁用)
+ winreg.SetValueEx(internet_settings, "ProxyEnable", 0, winreg.REG_DWORD, 0)
+ # 清空代理服务器信息
+ winreg.SetValueEx(internet_settings, "ProxyServer", 0, winreg.REG_SZ, "")
+
+ winreg.CloseKey(internet_settings)
+ print("Proxy disabled.")
+
+
+def initGame():
+ print("Init game.")
+ pass
+
+
+class App(customtkinter.CTk):
+ def __init__(self):
+ super().__init__()
+
+ self.title("dy-barrage-grab-log-window")
+ self.geometry("800x600")
+ self.grid_columnconfigure((0, 1), weight=1)
+ self.grid_rowconfigure(0, weight=1)
+ self.LogFrame = LogFrame(self)
+ self.LogFrame.grid(row=0, column=0, sticky="nsew")
+
+
+class LogFrame(customtkinter.CTkFrame):
+ def __init__(self, master):
+ super().__init__(master)
+
+ # 创建一个Frame来包含treeview和滚动条
+ self.tree_frame = ttk.Frame(self)
+ self.tree_frame.grid(row=0, column=0, sticky="nsew", padx=50, pady=50)
+
+ # 创建一个滚动条
+ self.scrollbar = customtkinter.CTkScrollbar(self.tree_frame)
+ self.scrollbar.grid(row=0, column=1, sticky="ns")
+
+ self.treeview = ttk.Treeview(
+ self.tree_frame,
+ columns=("Type", "User ID", "User Nickname", "Content"),
+ show="headings",
+ height=20,
+ yscrollcommand=self.scrollbar.set, # 将滚动条与treeview关联
+ )
+ self.treeview.grid(row=0, column=0, sticky="nsew")
+
+ # 设置滚动条的命令为移动treeview
+ self.scrollbar.configure(command=self.treeview.yview)
+
+ # 设置列宽
+ self.treeview.column("Type", width=50)
+ self.treeview.column("User ID", width=100)
+ self.treeview.column("User Nickname", width=100)
+ self.treeview.column("Content", width=450)
+
+ self.treeview.heading("Type", text="Type")
+ self.treeview.heading("User ID", text="User ID")
+ self.treeview.heading("User Nickname", text="User Nickname")
+ self.treeview.heading("Content", text="Content")
+
+ # 设置不同类型的颜色
+ self.treeview.tag_configure("type1", foreground="black")
+ self.treeview.tag_configure("type2", foreground="cyan")
+ self.treeview.tag_configure("type3", foreground="gold")
+ self.treeview.tag_configure("type4", foreground="blue")
+ self.treeview.tag_configure("type5", foreground="red")
+ self.treeview.tag_configure("type6", foreground="purple")
+ self.treeview.tag_configure("type7", foreground="green")
+ self.treeview.tag_configure("type8", foreground="orange")
+ self.treeview.tag_configure("type9", foreground="gray")
+ # 添加更多类型的颜色配置...
+
+ def display_message(self, message):
+ parsed_message = json.loads(message)
+ message_type = parsed_message.get("Type")
+
+ data_str = parsed_message.get("Data", "{}")
+ try:
+ data = json.loads(data_str)
+ except json.JSONDecodeError:
+ print(f"Cannot parse 'Data' as JSON: {data_str}")
+ return
+ user = data.get("User", {})
+ if user is None:
+ # print(f"'User' is None in 'Data': {data_str}")
+ user = {"Id": "无", "Nickname": "无"}
+ user_id = user.get("Id", "")
+ user_nickname = user.get("Nickname", "")
+ content = data.get("Content", "")
+ # 根据类型设置颜色
+ color_tag = f"type{message_type}"
+ message_type_str = [
+ "无",
+ "弹幕",
+ "点赞",
+ "进房",
+ "关注",
+ "礼物",
+ "统计",
+ "粉团",
+ "分享",
+ "下播",
+ ]
+ self.treeview.insert(
+ "",
+ "end",
+ values=(
+ message_type_str[message_type],
+ user_id,
+ user_nickname,
+ content,
+ ),
+ tags=(color_tag,),
+ )
+
+
+# async def send_ping(websocket):
+# while True:
+# try:
+# if websocket.open:
+# await websocket.ping()
+# await asyncio.sleep(5)
+# else:
+# break
+# except websockets.exceptions.ConnectionClosedOK:
+# # 连接已关闭,尝试重新连接
+# websocket = await websockets.connect("ws://127.0.0.1:8888")
+
+
+async def receive_messages():
+ while True:
+ try:
+ async with websockets.connect(
+ "ws://127.0.0.1:8888", ping_timeout=None, ping_interval=None
+ ) as websocket:
+ while True:
+ message = await websocket.recv()
+ if message is not None:
+ try:
+ json.loads(message)
+ app.LogFrame.display_message(message)
+ except json.JSONDecodeError:
+ print(
+ f"Received a message that could not be parsed as JSON: {message}"
+ )
+ except (
+ websockets.ConnectionClosed,
+ websockets.exceptions.ConnectionClosedError,
+ websockets.exceptions.ConnectionClosedOK,
+ ) as e:
+ print(f"Connection closed, retrying...{e}")
+ continue
+ except Exception as e:
+ print(f"An error occurred on ws server: {e}")
+ continue
+
+
+def main():
+ global listener_process
+ global app
+ # Check if the process is already running
+ for proc in psutil.process_iter():
+ if proc.name() == "WssBarrageServer.exe":
+ print("The process is already running.")
+ proc.kill()
+ time.sleep(1)
+
+ # Start a new process
+ # 将WssBarrageServer.exe放至同目录下dy-barrage-grab文件夹
+ # 获取当前脚本的绝对路径
+ script_dir = os.path.dirname(os.path.realpath(__file__))
+ # 构建WssBarrageServer.exe的绝对路径
+ exe_path = os.path.join(script_dir, "dy-barrage-grab", "WssBarrageServer.exe")
+
+ # 使用绝对路径启动进程
+ listener_process = subprocess.Popen(exe_path)
+ # 创建一个新的事件循环
+ loop = asyncio.new_event_loop()
+ # 设置这个事件循环为当前线程的事件循环
+ asyncio.set_event_loop(loop)
+ app = App()
+
+ # 点击关闭按钮时调用的函数
+ def on_close():
+ print("Closing...")
+ # 设置停止标志
+ stop_flag.set()
+ # 关闭WssBarrageServer
+ listener_process.terminate()
+ # 关闭代理
+ disable_proxy()
+ # 退出程序
+ os._exit(0)
+
+ # 设置关闭按钮的回调函数
+ app.protocol("WM_DELETE_WINDOW", on_close)
+ # 在事件循环中创建新的异步任务
+ loop.create_task(receive_messages())
+
+ def update():
+ # 更新Tkinter的界面
+ app.update()
+ # 在事件循环中定期调用update方法
+ loop.call_soon(update)
+
+ # 检查队列中是否有任务
+ while not gui_queue.empty():
+ # 获取任务并执行
+ task = gui_queue.get()
+ task()
+
+ # 在事件循环中定期调用update方法
+ loop.call_soon(update)
+ try:
+ # 运行事件循环
+ loop.run_forever()
+ except Exception as e:
+ print(f"An error occurred in loop: {e}")
+ finally:
+ # 关闭事件循环
+ loop.close()
+
+
+if __name__ == "__main__":
+ if is_admin():
+ # 如果已经是管理员权限,那么直接运行你的代码
+ print("Running as administrator.")
+ pass
+ else:
+ # 如果不是管理员权限,那么以管理员权限重新启动程序
+ print("Running as non-administrator.")
+ ctypes.windll.shell32.ShellExecuteW(
+ None, "runas", sys.executable, " ".join(sys.argv), None, 1
+ )
+ # try:
+ main()
+# except Exception as e:
+# print(f"An error occurred: {e}")
+# finally:
+# # 这里结束进程
+# listener_process.terminate()
+
+
+# 注册一个函数,在程序结束时调用
+def on_exit():
+ listener_process.terminate()
+
+
+atexit.register(on_exit)
diff --git a/README.md b/README.md
index c9249d0..559d37e 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,30 @@
-# 📺 抖音弹幕监听器
+📺 抖音弹幕监听器
+
+
+
+- 小白不圆 -
## 🔉前文提要
+ Github(受网络影响,更新很可能不及时)大佬请移步 [Gitee](https://gitee.com/haodong108/dy-barrage-grab) ,参与Issues讨论,QQ技术讨论群:
- + 群1: 819927029 (已满)
+ + 群1: 819927029
+ 群2: 789875441 (新建)
+ 注: (进群前请先⭐**Star**,并填入答案,用于排除行外人员,特殊原因请私聊我),之前因满员踢掉的群员可申请群2。
- + 开不起千人群😅
+ 发行版下载地址在[这里](https://gitee.com/haodong108/dy-barrage-grab/releases),别下成源码包了!!,**决定下载使用前请先仔细阅读文档**,不要进群提问已经介绍到的问题
-+ 💭 需要**多直播间超长稳定连接**、**自动私信**(支持群组,单发,支持多账号切换),**私信监听**、**自动关注**、**关注,粉丝群,作品点赞,评论监听**、**直播间发弹幕**、**获取榜单**、**直播间,用户主页信息**、**采集商品上架,连麦,PK等其他所有消息**、**作品评论(支持图片)**、 等功能的可以找我(QQ 1083092844)提供一些技术支持 (注:主要面向个人直播自动化,不面向恶意j流,不支持匿名直播间,想搞采集匿名的勿扰,加群也不会有人支持)
-+ **LiveIdea** 支持dou音Web全自动化的本地WebApi服务。直播、引留解放双手,需要了解详情请加我
-+ 支持接单,个人定制,dy相关优先,其他方面也可以找我帮忙转发定制,匿名直播间需求勿扰
-+ 那年,我与TA不可告人的秘密😏
++ 💭 需要抖音日常操作协议化,包括**多直播间超长稳定连接**、**自动私信**(支持群组,单发,支持多账号切换),**私信监听、自动关注、粉丝群、作品点赞评论及监听、直播间发弹幕、获取直播间榜单、直播间更多消息事件、作品评论(支持图片)、发布作品** 等等功能的可以找我(Q1083092844)提供一些技术支持 (注:主要面向个人直播自动化,不面向恶意j流、水jun等,不支持匿名直播间,想搞他人直播间匿名采集的勿扰,加群也不会有人支持)
++ 支持接单、定制等,dy相关优先,其他方面也可以找我帮忙转发定制
++ 如果好用,请捐赠个仨瓜俩枣,白某谢过各位老板
## ⛳近期更新
+2024-09-28 v2.7.6
+
+1. **[重要更新]** 支持免系统代理监听直播伴侣,需要在配置文件关闭`useproxy`选项,配置后监听web直播间可参考实践指南新部分
+2. 修复了 挂机检测Js拦截逻辑
+3. 默认关闭 禁用JS文件缓存 配置项,提高网页加载速度
+4. 礼物计数相关代码有一小处调整
+5. 整合了dll为单个exe文件
+
2024-05-16 v2.7.5
1. **[重要修复]** 修复了弹幕轮询响应解析Payload读取报错的问题
@@ -55,23 +65,6 @@
3. 支持了控制台隐藏,推送器弹幕类型过滤,日志弹幕类型过滤,弹幕文件日志 (见配置文件)
4. 支持了更多的ws命令,见 [Command.cs](./BarrageGrab/Modles/JsonEntity/Command.cs)
-2023-11-04 v2.6.9
-
-1. 修复了因抖音版本升级导致的WebRoomId获取不到的问题,并做了多重处理
-
-2023-09-24 v2.6.8
-
-1. 添加了上游代理支持,现在可以将无关的请求转发到其他的代理地址!详见下方配置说明`upstreamProxy`
-2. 内部添加了webRoomid 的缓存映射,只要打开直播间地址即可缓存WebRoomId和Roomid的映射关系,`WebRoomId`现已添加至ws弹幕流数据中!,需要注意直播伴侣端提取的弹幕当前不会有该字段,除非在开播后打开浏览器访问一次你的直播间
-3. MsgPack 现在添加了进程名字段`ProcessName`
-
-2023-08-17 v2.6.7
-
-1. [重要更新] 支持浏览器/客户端 http 弹幕监听,这也会解决原先版本因网络波动使抖音客户端弹幕获取方式降级,而获取不到弹幕导致必须刷新页面重新连接的问题。
-2. [重要更新] 原先的web/客户端 页面无操作检测逻辑因官方js变动而失效,本次更新修复且完善了该功能,现在会定时模拟全局按键、绕过js检测逻辑、置空Websocket.close 方法三重手段尽量避免断开连接。你也可以直接修改[直播页脚本](./BarrageGrab/Scripts/inject/livePage.js)而无需重新编译源码。
-3. ws连接现在默认监听在0.0.0.0地址,可以从其他客户端连接。
-4. 代理端口现在可选择不占用系统代理,便于某些用户直接使用浏览器代理获取弹幕(启动时携带参数,例如:"chrome.exe --proxy-server=127.0.0.1:8827",也可以在浏览器快捷方式-目标 引号之后附加该参数)
-
## 🖼️控制台截图
[](https://imgse.com/i/z9YYPU)
@@ -98,6 +91,8 @@
+
+
@@ -110,8 +105,6 @@
-
-
@@ -134,19 +127,20 @@
+
+
```
-### 推送数据格式
-
-+ 弹幕数据由WebSocket服务进行分发,使用Json格式进行推送,见项目 [BarrageMessages.cs](./BarrageGrab/Modles/JsonEntity/BarrageMessages.cs),如需调整请克隆项目后参照 [message.proto](./BarrageGrab/proto/message.proto) 进行源码修改调整,文件包含所有弹幕相关数据结构,可前往[ws在线测试](http://wstool.jackxiang.com/)网站,连接 ws://127.0.0.1:8888 进行测试
-
### 使用方法
-1. 管理员身份启动本程序,第一次启动会提示安装自签名证书,程序启动后挂在后台不要关,不然再打开会监听不到正在进行中的直播弹幕。
-2. 打开浏览器进入任何直播间进行测试,没有问题再启动直播伴侣
-3. 如果需要对接自己的应用程序,请先了解 [WebSocket](https://www.bilibili.com/video/BV1KN411n7WD/?share_source=copy_web&vd_source=39ebf42c923024b9eddeca32c9e46d16) ,然后进行对接,可前往[这里](http://wstool.jackxiang.com/)测试
+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转换函数,详见其注释
@@ -177,29 +171,59 @@
+ **弹幕断连**
-
+ 如果浏览器总是连接一段时间后,就在在控制台看不到弹幕了,优先使用Chrome 和 Edge 浏览器,其他浏览器(例如360浏览器) 可能会存在该问题
-
+
+ 尝试打开配置文件 `forcePolling` 选项,情况获取会有所改善
-
+
+ 其他可能是网络原因,或者直播间断连导致,正在想办法优化解决中。。。需要长时间连接可以找我使用其他方式,支持直连和轮询获取方式
-
+ **程序关闭后无法上网**
+ 这是因为本程序运行时会占用系统代理,不正确的关闭方式可能会导致系统代理未能正确关闭,请前往找到 系统设置-网络和Internet-代理-使用代理服务器 并关闭它即可,也可管理员运行[关闭代理.bat](./关闭代理.bat)
++ **未开系统代理,但是无法监听到直播伴侣弹幕**
+
+ + 之前可以突然不行,且刚刚升级直播伴侣,请重监听启程序和直播伴侣再次尝试
+ + 如果一次都没成功,则需要检查启动菜单是否有直播伴侣的快捷方式,换言之则必须确保 `C:\ProgramData\Microsoft\Windows\Start Menu\Programs\直播伴侣.lnk` 文件存在,否则请前往配置文件显式指定`直播伴侣.exe`文件存储路径,见配置项`liveCompanPath`
+
## 🎲 实践指南
+
+### 日常使用
+
+ **隐藏控制台**:见配置文件 `hideConsole`
+ **远程控制程序**:例如远程关闭,隐藏/显示控制台,启用/关闭系统代理,可发送Json数据包 `{"Cmd":xx,"Data":xx}` 到ws推送地址,数据格式详见: [Command.cs](./BarrageGrab/Modles/JsonEntity/Command.cs)
+ **启用弹幕文件日志**:见配置文件 `barrageFileLog`,启用后,抓到弹幕会记录在根目录 logs/弹幕日志 文件夹中,支持类型过滤,见`logFilter` 配置项
+ **UI界面**: 见配置文件 `showWindow ` (做的仓促,功能简陋)
+ **上游代理**:一般用于自己有 代理工具/代理服务器,希望在使用本程序时访问外网不受到影响,见配置文件项`upstreamProxy` 将它改为你代理工具的代理地址
-+ **局域代理**: 若是只需要监听了浏览器,且不想占用系统代理,可创建一个浏览器快捷方式,打开属性,并在exe文件路径后附加启动参数,`--proxy-server=127.0.0.1:8827`,例如:`"C:\\xxx\chrome.exe" --proxy-server=127.0.0.1:8827`
++ **局域代理**: (请优先参考下文 **浏览器代理切换扩展**) 可创建一个浏览器快捷方式,打开属性,并在exe文件路径后附加启动参数,`--proxy-server=127.0.0.1:8827`,例如:`"C:\\xxx\chrome.exe" --proxy-server=127.0.0.1:8827`
+ **直播间区分**: 推送包含 `Onwer`字段,用户描述主播基本信息,客户端可以优先使用其中的`SecUid`或者`Uid`作为直播间标识,若使用`WebRoomid`则需要注意直播伴侣进程需要特殊处理,因为直播伴侣推送的WebRoomid是用户的抖音号。`RoomId`只能作为场次标识,每次开播都会不一样,不可用作直播间唯一区分
+ **直播间地址**: 冷知识,使用`https://live.douyin.com/<抖音号>` 也可以进入主播直播间
+### 浏览器代理切换扩展
+
+若关闭了`usedProxy`开关,浏览器需要通过显示指定代理地址完成代理指向操作,此时可以通过浏览器扩展很好的完成这项功能,以Edge为例
+
+1. 前往扩展商店 搜索 `switchyomega` 安装
+
+ [
](https://imgse.com/i/pA1SSSg)
+
+2. 添加弹幕代理
+
+ [
](https://imgse.com/i/pA1SCOs)
+
+3. 配置代理地址,并保存,不要配置为ws服务端口地址,程序默认代理端口是**8827**
+
+ [
](https://imgse.com/i/pA1SLjJ)
+
+4. 进入直播页,应用代理,完成!
+
+ [
](https://imgse.com/i/pA1SXu9)
+
+5. Chrome 用户请自行百度搜索扩展安装方法
+
+
+
## ⚠️特别注意
@@ -227,8 +251,9 @@
+ 请详细阅读[免责声明](./免责声明.txt)条款后进行使用
+
## 🍻支持一下?
-开源不易啊老板
+开源不易,老板
\ No newline at end of file