diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0757b5b9..65bde9d06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,3 +78,5 @@
- Fix( #364 ),兼容青龙异形response数据类型
- Fix( #366 #361 ),修复一些低级bug
- Feature( #359 ),兼容读取不到`$QL_DIR`的情况
+## 0.4.0
+- 合并PR( #381 #383 ),新增直播间挂机功能,感谢@bakapiano
\ No newline at end of file
diff --git a/README.md b/README.md
index b0a26ee7d..d6076935c 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ BiliBiliTool
- **扫码登录,自动更新cookie**
- **每日获取满额升级经验(登录、投币、点赞、分享视频)(支持指定支持up主)**
+- **直播间挂机**
- **每天漫画签到**
- **每天直播签到**
- **直播中心银瓜子兑换为硬币**
@@ -69,7 +70,7 @@ BiliBiliTool
- **本应用仅用于学习和测试,作者本人并不对其负责,请于运行测试完成后自行删除,请勿滥用!**
- **所有代码都是开源且透明的,任何人均可查看,程序不会保存或滥用任何用户的个人信息**
-- **应用内几乎所有功能都开放为了配置(如任务开关、日期、id等),详细信息可阅读配置文档,请自己对配置负责**
+- **应用内几乎所有功能都开放为了配置(如任务开关、日期、id等),详细信息可阅读配置文档,请对自己配置负责**
本地运行图示:
@@ -111,9 +112,7 @@ BiliBiliTool 实现自动完成任务的原理,是通过调用一系列开放
#### 1.1.5. 方式五:~~GitHub Actions~~
-暂时删掉该方式避避风头。
-
-**建议所有使用该方式运行的朋友,暂时先替换其他运行方式,避免造成不必要的损失。**
+GitHub官方反对并抵制对Actions的滥用,建议所有使用该方式运行的朋友,暂时先替换其他运行方式,避免封号。
#### 1.1.6. 方式六:Chart部署
@@ -146,15 +145,15 @@ dotnet Ray.BiliBiliTool.Console.dll --runTasks=Daily&LiveLottery
任务列表如下:
-| 任务名 | Code | 功能 | 默认WorkFlow文件 | GithHub Environments | 推荐运行频率 | 备注 |
-| :----: | :----: | :----: | :----: | :----: | :----: | :----: |
-| 扫码登录 | Login | 试用bili app扫码登录,用于第一次运行时初始化cookie,或cookie过期时的更新。不同平台会将cookie存储到不同地方,青龙存储到环境变量中,其他会存储到cookies。json中 | | Production | 手动 | |
-| 每日任务 | Daily | 完成每日任务获取满额65点经验(登录、观看视频、分享视频、投币),以及签到、领福利和充电等附属功能 | bilibili-daily-task.yml | Production | 每天一次 | |
-| 天选时刻抽奖 | LiveLottery | 直播中心天选时刻抽奖 | live-lottery-task.yml | LiveLottery | 建议每天运行0-4次内 | 对应Actions工作流默认是关闭的,需要添加key为`ISOPENLIVELOTTERYTASK`、值为`true`的secret来手动开启;大部分抽奖都需要关注主播,介意的不要开启 |
-| 批量取关 | UnfollowBatched | 批量取关指定分组下的所有关注(主要用于清理天选抽奖而产生的关注) | unfollow-batched-task.yml | 无 | 需要时手动运行 | 需要通过配置指定2个参数:`GroupName`(分组名称,如`天选时刻`)和`Count`(目标取关个数,-1表示全部),应用会倒序从后往前取关指定个数 |
-| 大会员大积分 | VipBigPoint | 大会员大积分任务(签到、浏览、观看) | 无 | 无 | 每天凌晨一点运行 | |
-| 测试Cookie | Test | 测试Cookie是否正常 | 无,可以使用empty-task.yml来运行 | 无 | 需要时手动运行 | 主要用于调试 |
-| 空模板 | 无(只用于 GitHub Actions ) | 用于 GitHub Actions 运行指定的任意任务 | empty-task.yml | 无 | 需要时手动运行 | 需要通过配置指定要运行的任务Code(多个使用英文逗号分隔),主要用于调试 |
+| 任务名 | Code | 功能 | 推荐运行频率 | 备注 |
+| :----: | :----: | :----: | :----: | :----: |
+| 扫码登录 | Login | 试用bili app扫码登录,用于第一次运行时初始化cookie,或cookie过期时的更新。不同平台会将cookie存储到不同地方,青龙存储到环境变量中,其他会存储到cookies。json中 | 手动 | |
+| 每日任务 | Daily | 完成每日任务获取满额65点经验(登录、观看视频、分享视频、投币),以及签到、领福利和充电等附属功能 | 每天一次 | |
+| 天选时刻抽奖 | LiveLottery | 直播中心天选时刻抽奖 | 建议每天运行0-4次 | 对应Actions工作流默认是关闭的,需要添加key为`ISOPENLIVELOTTERYTASK`、值为`true`的secret来手动开启;大部分抽奖都需要关注主播,介意的不要开启 |
+| 批量取关 | UnfollowBatched | 批量取关指定分组下的所有关注(主要用于清理天选抽奖而产生的关注) | 需要时手动运行 | 需要通过配置指定2个参数:`GroupName`(分组名称,如`天选时刻`)和`Count`(目标取关个数,-1表示全部),应用会倒序从后往前取关指定个数 |
+| 大会员大积分 | VipBigPoint | 大会员大积分任务(签到、浏览、观看) | 每天凌晨一点运行 | |
+| 直播间挂机 | LiveFansMedal | 直播间挂机 | 每天一次 | |
+| 测试Cookie | Test | 测试Cookie是否正常 | 需要时手动运行 | 主要用于调试 |
## 3. 个性化自定义配置
@@ -210,9 +209,9 @@ dotnet Ray.BiliBiliTool.Console.dll --runTasks=Daily&LiveLottery
* 搜索查看 Issue,确定是否已有人提过同类问题
-* 确认没有同类 Issue 后,自己可新建 Issue,描述问题或建议
+* 对于不确定的主题,为避免code结束后PR不被接受,可以先新建 Issue,描述问题或建议,讨论清楚后再动手编码
-* 如果想自己解决,请 Fork 仓库后,在**develop 分支**进行编码开发,完成后**提交 PR 到 develop 分支**,并标注解决的 Issue 编号
+* 如果确认自己可以解决,请 Fork 仓库后,在**develop 分支**进行编码开发,完成后**提交 PR 到 develop 分支**
我会尽快进行代码审核,测试成功后会合并入 main 主分支,提前感谢您的贡献。
@@ -221,8 +220,6 @@ dotnet Ray.BiliBiliTool.Console.dll --runTasks=Daily&LiveLottery
## 8. 捐赠支持
-[>>捐赠留言及回复](docs/donate-list.md)
-
个人维护开源不易
如果觉得我写的程序对你小有帮助
diff --git a/Ray.BiliBiliTool.sln b/Ray.BiliBiliTool.sln
index 274a807e1..ec70453b6 100644
--- a/Ray.BiliBiliTool.sln
+++ b/Ray.BiliBiliTool.sln
@@ -151,7 +151,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ray.Serilog.Sinks.WorkWeiXi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ray.Serilog.Sinks.MicrosoftTeamsBatched", "src\Ray.Serilog.Sinks\Ray.Serilog.Sinks.MicrosoftTeamsBatched\Ray.Serilog.Sinks.MicrosoftTeamsBatched.csproj", "{FB9A43DE-00F0-42C4-BF92-AF61D752CCA2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ray.Serilog.Sinks.GotifyBatched", "src\Ray.Serilog.Sinks\Ray.Serilog.Sinks.GotifyBatched\Ray.Serilog.Sinks.GotifyBatched.csproj", "{B00FF75D-4C48-45ED-9A24-5C0D383317EE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ray.Serilog.Sinks.GotifyBatched", "src\Ray.Serilog.Sinks\Ray.Serilog.Sinks.GotifyBatched\Ray.Serilog.Sinks.GotifyBatched.csproj", "{B00FF75D-4C48-45ED-9A24-5C0D383317EE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -254,7 +254,7 @@ Global
{DB227D60-0737-45C2-8CEA-F55FDA711301} = {38736647-2196-417E-8519-C48A012A63D9}
{114D45C8-E4BB-47EE-89AC-BD1DC6FA3BAD} = {E9BDDCBE-A57D-4E3B-8252-708088386ADF}
{2039BF6A-5EC4-439C-8D2E-77313075843A} = {E9BDDCBE-A57D-4E3B-8252-708088386ADF}
- {110D3D21-8E9B-45AB-9667-6DA1DB3862AC} = {AF21E067-3307-4E7F-8CE8-C695E6B61876}
+ {110D3D21-8E9B-45AB-9667-6DA1DB3862AC} = {120917DC-474C-448B-9EBB-1B3BA3A20B9D}
{7188698C-0A9A-43B2-B3E2-5136B14FDE13} = {110D3D21-8E9B-45AB-9667-6DA1DB3862AC}
{3388A58D-91CC-4875-A29F-3E6FC3B44BF5} = {98051127-2868-4F5C-9B2C-2150975E05F3}
{B6AEDD60-9C06-4391-9171-65EBD5E9D77A} = {98051127-2868-4F5C-9B2C-2150975E05F3}
diff --git a/common.props b/common.props
index bb7c57498..8693d4eb0 100644
--- a/common.props
+++ b/common.props
@@ -1,7 +1,7 @@
Ray
- 0.3.2
+ 0.4.0
$(NoWarn);CS1591;CS0436
diff --git a/docker/crontab b/docker/crontab
index de72028a6..4b39fc56f 100644
--- a/docker/crontab
+++ b/docker/crontab
@@ -1,3 +1,4 @@
0 15 * * * dotnet /app/Ray.BiliBiliTool.Console.dll --runTasks=Daily >> /var/log/cron.log
0 22 * * * dotnet /app/Ray.BiliBiliTool.Console.dll --runTasks=LiveLottery >> /var/log/cron.log
7 1 * * * dotnet /app/Ray.BiliBiliTool.Console.dll --runTasks=VipBigPoint >> /var/log/cron.log
+5 0 * * * dotnet /app/Ray.BiliBiliTool.Console.dll --runTasks=LiveFansMedal >> /var/log/cron.log
diff --git a/docker/sample/docker-compose.yml b/docker/sample/docker-compose.yml
index bca5efa0e..c6b7a4f2a 100644
--- a/docker/sample/docker-compose.yml
+++ b/docker/sample/docker-compose.yml
@@ -19,6 +19,7 @@ services:
- Ray_LiveLotteryTaskConfig__Cron=0 22 * * *
- Ray_UnfollowBatchedTaskConfig__Cron=0 6 1 * *
- Ray_VipBigPointConfig__Cron=7 1 * * *
+ - Ray_LiveFansMedalTaskConfig__Cron=5 0 * * *
# UA:
- Ray_Security__UserAgent=
diff --git a/docker/scripts/entry.sh b/docker/scripts/entry.sh
index 2a238d382..ff6d47ecb 100644
--- a/docker/scripts/entry.sh
+++ b/docker/scripts/entry.sh
@@ -8,35 +8,38 @@ CRON_FILE="/etc/cron.d/bilicron"
# https://stackoverflow.com/questions/27771781/how-can-i-access-docker-set-environment-variables-from-a-cron-job
echo "[step 1/4]导入环境变量"
-printenv | grep -v "no_proxy" > /etc/environment
-declare -p | grep -v "no_proxy" > /etc/cron.env
+printenv | grep -v "no_proxy" >/etc/environment
+declare -p | grep -v "no_proxy" >/etc/cron.env
echo -e "=>完成\n"
echo "[step 2/4]配置cron定时任务"
-echo "SHELL=/bin/bash" > $CRON_FILE
-echo "BASH_ENV=/etc/cron.env" >> $CRON_FILE
-if [ -z "$Ray_LiveLotteryTaskConfig__Cron$Ray_UnfollowBatchedTaskConfig__Cron$Ray_VipBigPointConfig__Cron" ]; then
+echo "SHELL=/bin/bash" >$CRON_FILE
+echo "BASH_ENV=/etc/cron.env" >>$CRON_FILE
+if [ -z "$Ray_LiveLotteryTaskConfig__Cron$Ray_UnfollowBatchedTaskConfig__Cron$Ray_VipBigPointConfig__Cron$Ray_LiveFansMedalTaskConfig__Cron" ]; then
echo "=>使用默认的定时任务配置"
- cat /app/scripts/crontab >> $CRON_FILE
+ cat /app/scripts/crontab >>$CRON_FILE
else
echo "=>使用用户指定的定时任务配置"
if ! [ -z "$Ray_DailyTaskConfig__Cron" ]; then
- echo "$Ray_DailyTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=Daily" >> $CRON_FILE
+ echo "$Ray_DailyTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=Daily" >>$CRON_FILE
fi
if ! [ -z "$Ray_LiveLotteryTaskConfig__Cron" ]; then
- echo "$Ray_LiveLotteryTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=LiveLottery" >> $CRON_FILE
+ echo "$Ray_LiveLotteryTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=LiveLottery" >>$CRON_FILE
fi
if ! [ -z "$Ray_UnfollowBatchedTaskConfig__Cron" ]; then
- echo "$Ray_UnfollowBatchedTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=UnfollowBatched" >> $CRON_FILE
+ echo "$Ray_UnfollowBatchedTaskConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=UnfollowBatched" >>$CRON_FILE
fi
if ! [ -z "$Ray_VipBigPointConfig__Cron" ]; then
- echo "$Ray_VipBigPointConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=VipBigPoint" >> $CRON_FILE
+ echo "$Ray_VipBigPointConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=VipBigPoint" >>$CRON_FILE
+ fi
+ if ! [ -z "$Ray_LiveFansMedalConfig__Cron" ]; then
+ echo "$Ray_LiveFansMedalConfig__Cron cd /app && dotnet $CONSOLE_DLL --runTasks=LiveFansMedal" >>$CRON_FILE
fi
fi
if ! [ -z "$Ray_Crontab" ]; then
echo "=>检测到自定义定时任务"
- echo "$Ray_Crontab" >> $CRON_FILE
+ echo "$Ray_Crontab" >>$CRON_FILE
fi
cat $CRON_FILE
@@ -57,5 +60,5 @@ echo -e "[step 全部已完成]\n"
. /app/scripts/entry_after.sh
-touch /var/log/cron.log #todo:debian似乎并没有记录cron的日志。。。
-tail -f /var/log/cron.log # 追踪cron日志,避免当前脚本终止导致容器终止
\ No newline at end of file
+touch /var/log/cron.log #todo:debian似乎并没有记录cron的日志。。。
+tail -f /var/log/cron.log # 追踪cron日志,避免当前脚本终止导致容器终止
diff --git a/qinglong/DefaultTasks/bili_task_liveFansMedal.sh b/qinglong/DefaultTasks/bili_task_liveFansMedal.sh
new file mode 100644
index 000000000..aea011d95
--- /dev/null
+++ b/qinglong/DefaultTasks/bili_task_liveFansMedal.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# new Env("bili直播粉丝牌")
+# cron 5 0 * * * bili_task_liveFansMedal.sh
+. bili_task_base.sh
+
+cd ./src/Ray.BiliBiliTool.Console
+
+export Ray_RunTasks=LiveFansMedal && \
+dotnet run --ENVIRONMENT=Production
diff --git a/qinglong/DefaultTasks/dev/bili_dev_task_liveFansMedal.sh b/qinglong/DefaultTasks/dev/bili_dev_task_liveFansMedal.sh
new file mode 100644
index 000000000..54d9d76e6
--- /dev/null
+++ b/qinglong/DefaultTasks/dev/bili_dev_task_liveFansMedal.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# new Env("bili直播粉丝牌[dev先行版]")
+# cron 5 0 * * * bili_dev_task_liveFansMedal.sh
+. bili_dev_task_base.sh
+
+cd ./src/Ray.BiliBiliTool.Console
+
+export Ray_RunTasks=LiveFansMedal && \
+dotnet run --ENVIRONMENT=Production
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/GetSpaceInfoResponse.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/GetSpaceInfoResponse.cs
new file mode 100644
index 000000000..478a00482
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/GetSpaceInfoResponse.cs
@@ -0,0 +1,18 @@
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos
+{
+ public class GetSpaceInfoResponse
+ {
+ public int Mid { get; set; }
+
+ public string Name { get; set; }
+
+ public SpaceLiveRoomInfoDto Live_room { get; set; }
+ }
+
+ public class SpaceLiveRoomInfoDto
+ {
+ public string Title { get; set; }
+
+ public int Roomid { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/EnterRoomRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/EnterRoomRequest.cs
new file mode 100644
index 000000000..e53b825ed
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/EnterRoomRequest.cs
@@ -0,0 +1,56 @@
+using Newtonsoft.Json;
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class EnterRoomRequest
+ {
+ public EnterRoomRequest(
+ int roomId,
+ int parentId,
+ int areaID,
+ int seqNumber, // 心跳包编号
+ long timestamp,
+ string userAgent,
+ string csrf,
+ int ruid,
+ string device)
+ {
+ Id = JsonConvert.SerializeObject(new[] { parentId, areaID, seqNumber, roomId });
+ Ts = timestamp;
+ Ua = userAgent;
+ Csrf = csrf;
+ Ruid = ruid;
+
+ Is_patch = 0;
+ Heart_beat = "[]";
+ Visit_id = "";
+ Device = device;
+ }
+ public string Id { get; set; }
+
+ public int Ruid { get; set; }
+
+ public long Ts { get; set; }
+
+ public int Is_patch { get; set; }
+
+ public string Heart_beat { get; set; }
+
+ public string Ua { get; set; }
+
+ public string Csrf_token => Csrf;
+
+ public string Csrf { get; set; }
+
+ public string Visit_id { get; set; }
+
+ public string Device { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/GetLiveRoomInfoResponse.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/GetLiveRoomInfoResponse.cs
new file mode 100644
index 000000000..7883dbcc6
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/GetLiveRoomInfoResponse.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class GetLiveRoomInfoResponse
+ {
+ public int Room_id { get; set; }
+
+ public int Area_id { get; set; }
+
+ public int Parent_area_id { get; set; }
+
+ public int Uid { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatRequest.cs
new file mode 100644
index 000000000..acdb9a995
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatRequest.cs
@@ -0,0 +1,76 @@
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Utils;
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class HeartBeatRequest
+ {
+ public HeartBeatRequest(
+ int roomId,
+ int parentId,
+ int areaID,
+ int seqNumber, // 心跳包编号
+ string buvid, // cookie['LIVE_BUVID']
+ long timestamp,
+ long ets, // 由后端返回的 timestamp
+ string userAgent,
+ ICollection secretRule,
+ string secretKey,
+ string csrf,
+ string uuid,
+ string device)
+ {
+ Id = JsonSerializer.Serialize(new[] { parentId, areaID, seqNumber, roomId });
+ Ets = ets;
+ Benchmark = secretKey;
+ Time = 60;
+ Ts = timestamp;
+ Ua = userAgent;
+ Csrf = csrf;
+ Device = device;
+
+ // 构造哈希值
+ var json = new
+ {
+ platform = "web",
+ parent_id = parentId,
+ area_id = areaID,
+ seq_id = seqNumber,
+ room_id = roomId,
+ buvid,
+ uuid,
+ ets,
+ time = 60,
+ ts = timestamp,
+ };
+ string jsonString = JsonSerializer.Serialize(json);
+ this.S = LiveHeartBeatCrypto.Sypder(jsonString, secretRule, secretKey);
+
+ this.Visit_id = "";
+ }
+
+ public string S { get; set; }
+
+ public string Id { get; set; }
+
+ public long Ets { get; set; }
+
+ public string Benchmark { get; set; }
+
+ public long Time { get; set; }
+
+ public long Ts { get; set; }
+
+ public string Ua { get; set; }
+
+ public string Csrf_token => Csrf;
+
+ public string Csrf { get; set; }
+
+ public string Visit_id { get; set; }
+
+ public string Device { get; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatResponse.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatResponse.cs
new file mode 100644
index 000000000..fc72a2da6
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/HeartBeatResponse.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class HeartBeatResponse
+ {
+ public int Heartbeat_interval { get; set; }
+
+ public string Secret_key { get; set; }
+
+ public List Secret_rule { get; set;}
+
+ public long Timestamp { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/LikeLiveRoomRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/LikeLiveRoomRequest.cs
new file mode 100644
index 000000000..fe55938c6
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/LikeLiveRoomRequest.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class LikeLiveRoomRequest
+ {
+ public LikeLiveRoomRequest(int roomid, string csrf)
+ {
+ Roomid = roomid;
+ Csrf= csrf;
+ }
+
+ public int Roomid { get; set; }
+
+ public string Csrf { get; set; }
+
+ public string Csrf_token => Csrf;
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/MedalWallDto.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/MedalWallDto.cs
new file mode 100644
index 000000000..bfd5bb547
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/MedalWallDto.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class MedalWallResponse
+ {
+ public List List { get; set; }
+ }
+
+ public class MedalWallDto
+ {
+ public int Live_status { get; set; }
+
+ public string Target_name { get; set; }
+
+ public string Link { get; set; }
+
+ public MedalInfoDto Medal_info { get; set; }
+ }
+
+ public class MedalInfoDto
+ {
+ public string Medal_name { get; set; }
+
+ public int Medal_id { get; set; }
+
+ public int Target_id { get; set; }
+
+ public int Level { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/SendLiveDanmukuRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/SendLiveDanmukuRequest.cs
new file mode 100644
index 000000000..b9c0094bb
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/SendLiveDanmukuRequest.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class SendLiveDanmukuRequest
+ {
+ public SendLiveDanmukuRequest(string csrf, int room_id, string message)
+ {
+ this.Csrf = csrf;
+ this.Msg= message;
+ this.Roomid= room_id;
+ this.Bubble = "0";
+ this.Mode = "1";
+ this.Fontsize = "25";
+ this.Rnd = "1672305761";
+ this.Color = "16777215";
+ }
+ public string Bubble { get; set; }
+
+ public string Msg { get; set; }
+
+ public string Color { get; set; }
+
+ public string Mode { get; set; }
+
+ public string Fontsize { get; set; }
+
+ public string Rnd { get; set; }
+
+ public int Roomid { get; set; }
+
+ public string Csrf { get; set; }
+
+ public string Csrf_token => Csrf;
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WearMedalWallRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WearMedalWallRequest.cs
new file mode 100644
index 000000000..97e76a62f
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WearMedalWallRequest.cs
@@ -0,0 +1,43 @@
+using Ray.BiliBiliTool.Infrastructure.Helpers;
+using System;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class WearMedalWallRequest
+ {
+ public WearMedalWallRequest(string csrf, int medal_id)
+ {
+ Csrf = csrf;
+ Medal_id = medal_id;
+ }
+
+ public int Medal_id { get; set; }
+
+ public string Csrf { get; set; }
+
+ public string Csrf_token => Csrf;
+
+ ///
+ ///
+ ///
+ /// 8u0w3cesz1o0
+ /// 33moy4vugle0
+ /// 9zys612vo0c0
+ /// 3uu2mkxt21c0
+ /// 8orqn5vf4i00
+ public string Visit_id { get; set; } = _visitId;//todo
+
+ public static string GetRandomVisitId()
+ {
+ var ran = new Random();
+ int first = ran.Next(1, 10);
+ int last = 0;
+
+ var s = new RandomHelper().GenerateCode(10).ToLower();
+
+ return $"{first}{s}{last}";
+ }
+
+ private static string _visitId = GetRandomVisitId();
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatRequest.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatRequest.cs
new file mode 100644
index 000000000..56caa1faa
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatRequest.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class WebHeartBeatRequest
+ {
+ public WebHeartBeatRequest(int room_id, int next_interval)
+ {
+ this.RoomId = room_id;
+ this.NextInterval = next_interval;
+ }
+
+ public int RoomId { set; get; }
+
+ public int NextInterval { set; get; }
+
+ public override string ToString()
+ {
+ string arg = $"{this.NextInterval}|{this.RoomId}|1|0";
+ return Convert.ToBase64String(Encoding.UTF8.GetBytes(arg));
+ }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatResponse.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatResponse.cs
new file mode 100644
index 000000000..14e706488
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Dtos/Live/WebHeartBeatResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live
+{
+ public class WebHeartBeatResponse
+ {
+ public int Next_interval { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveApi.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveApi.cs
index f7036ca1e..c09372450 100644
--- a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveApi.cs
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveApi.cs
@@ -1,4 +1,5 @@
using System;
+using System.Net.Http;
using System.Threading.Tasks;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
@@ -52,7 +53,7 @@ public interface ILiveApi : IBiliBiliApi
[Header("Content-Type", "application/x-www-form-urlencoded")]
[Header("Origin", "https://link.bilibili.com")]
[HttpPost("/xlive/revenue/v1/wallet/silver2coin")]
- Task> Silver2Coin([FormContent]Silver2CoinRequest request);
+ Task> Silver2Coin([FormContent] Silver2CoinRequest request);
///
/// 获取直播中心钱包状态
@@ -97,5 +98,55 @@ public interface ILiveApi : IBiliBiliApi
///
[HttpPost("/xlive/lottery-interface/v1/Anchor/Join")]
Task> Join([FormContent] JoinTianXuanRequest request);
+
+ ///
+ /// 获取用户的粉丝勋章
+ ///
+ /// uid
+ ///
+ [Header("Referer", "https://live.bilibili.com/")]
+ [Header("Origin", "https://live.bilibili.com")]
+ [HttpGet("/xlive/web-ucenter/user/MedalWall?target_id={userId}")]
+ Task> GetMedalWall(int userId);
+
+ ///
+ /// 佩戴粉丝勋章
+ ///
+ /// uid
+ ///
+ [Header("Referer", "https://live.bilibili.com/")]
+ [Header("Origin", "https://live.bilibili.com")]
+ [HttpPost("/xlive/app-ucenter/v1/fansMedal/wear")]
+ Task WearMedalWall([FormContent] WearMedalWallRequest request);
+
+ ///
+ /// 发送弹幕
+ ///
+ /// request
+ ///
+ [HttpPost("/msg/send")]
+ Task SendLiveDanmuku([FormContent] SendLiveDanmukuRequest request);
+
+ ///
+ /// 获取直播间信息
+ ///
+ /// roomId
+ ///
+ [HttpGet("/room/v1/Room/get_info?room_id={roomId}&from=room")]
+ Task> GetLiveRoomInfo(int roomId);
+
+ ///
+ /// 请求直播主页用于配置直播相关 Cookie
+ ///
+ [HttpGet("/news/v1/notice/recom?product=live")]
+ Task GetLiveHome();
+
+ ///
+ /// 点赞直播间
+ ///
+ [HttpPost("/xlive/web-ucenter/v1/interact/likeInteract")]
+ [Header("Referer", "https://live.bilibili.com/")]
+ [Header("Origin", "https://live.bilibili.com")]
+ Task LikeLiveRoom([FormContent] LikeLiveRoomRequest request);
}
}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveTraceApi.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveTraceApi.cs
new file mode 100644
index 000000000..c5a36904c
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/ILiveTraceApi.cs
@@ -0,0 +1,22 @@
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
+using Ray.BiliBiliTool.Config.Options;
+using System.Threading.Tasks;
+using WebApiClientCore.Attributes;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces
+{
+ [Header("Host", "live-trace.bilibili.com")]
+ public interface ILiveTraceApi : IBiliBiliApi
+ {
+
+ [HttpGet("/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb={request}&pf=web")]
+ Task> WebHeartBeat(WebHeartBeatRequest request);
+
+ [HttpPost("/xlive/data-interface/v1/x25Kn/E")]
+ Task> EnterRoom([FormContent] EnterRoomRequest request);
+
+ [HttpPost("/xlive/data-interface/v1/x25Kn/X")]
+ Task> HeartBeat([FormContent] HeartBeatRequest request);
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/IUserInfoApi.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/IUserInfoApi.cs
index 2687a4d57..45f425118 100644
--- a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/IUserInfoApi.cs
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/IUserInfoApi.cs
@@ -19,5 +19,13 @@ public interface IUserInfoApi : IBiliBiliApi
///
[HttpGet("/x/web-interface/nav")]
Task> LoginByCookie();
+
+ ///
+ /// 获取用户空间信息
+ ///
+ /// uid
+ ///
+ [HttpGet("/x/space/wbi/acc/info?mid={userId}")]
+ Task> GetSpaceInfo(int userId);
}
}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Utils/LiveHeartBeatCrypto.cs b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Utils/LiveHeartBeatCrypto.cs
new file mode 100644
index 000000000..08ea68316
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Utils/LiveHeartBeatCrypto.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Utils
+{
+ public class LiveHeartBeatCrypto
+ {
+ public static string Sypder(string text, ICollection rules, string key)
+ {
+ string result = text;
+ foreach(var rule in rules)
+ {
+ switch (rule)
+ {
+ case 0:
+ result = Hash(result, key, "HMACMD5");
+ break;
+ case 1:
+ result = Hash(result, key, "HMACSHA1");
+ break;
+ case 2:
+ result = Hash(result, key, "HMACSHA256");
+ break;
+ case 3:
+ result = Hash(result, key, "HMACSHA224");
+ break;
+ case 4:
+ result = Hash(result, key, "HMACSHA512");
+ break;
+ case 5:
+ result = Hash(result, key, "HMACSHA384");
+ break;
+ default:
+ break;
+ }
+ }
+ return result;
+ }
+
+ private static string Hash(string text, string key, string algorithmName)
+ {
+ var hamc = HMAC.Create(algorithmName);
+ hamc.Key = Encoding.UTF8.GetBytes(key);
+ byte[] inArray = hamc.ComputeHash(Encoding.UTF8.GetBytes(text));
+ return BitConverter.ToString(inArray).Replace("-", "").ToLower();
+ }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Agent/BiliCookie.cs b/src/Ray.BiliBiliTool.Agent/BiliCookie.cs
index 46d324c81..a60acaf07 100644
--- a/src/Ray.BiliBiliTool.Agent/BiliCookie.cs
+++ b/src/Ray.BiliBiliTool.Agent/BiliCookie.cs
@@ -40,6 +40,10 @@ private BiliCookie(ILogger logger, string ckStr)
{
SessData = sess;
}
+ if (CookieItemDictionary.TryGetValue(GetPropertyDescription(nameof(LiveBuvid)), out string liveBuvid))
+ {
+ LiveBuvid = liveBuvid;
+ }
}
[Description("DedeUserID")]
@@ -54,6 +58,9 @@ private BiliCookie(ILogger logger, string ckStr)
[Description("bili_jct")]
public string BiliJct { get; set; }
+ [Description("LIVE_BUVID")]
+ public string LiveBuvid { get; set; }
+
///
/// 检查是否已配置
///
diff --git a/src/Ray.BiliBiliTool.Agent/Extensions/ServiceCollectionExtension.cs b/src/Ray.BiliBiliTool.Agent/Extensions/ServiceCollectionExtension.cs
index 36d17512e..ec9b0d0e5 100644
--- a/src/Ray.BiliBiliTool.Agent/Extensions/ServiceCollectionExtension.cs
+++ b/src/Ray.BiliBiliTool.Agent/Extensions/ServiceCollectionExtension.cs
@@ -72,6 +72,8 @@ public static IServiceCollection AddBiliBiliClientApi(this IServiceCollection se
services.AddBiliBiliClientApi("http://passport.bilibili.com", false);
+ services.AddBiliBiliClientApi("https://live-trace.bilibili.com");
+
//qinglong
var qinglongHost = configuration["QL_URL"]?? "http://localhost:5600";
services
diff --git a/src/Ray.BiliBiliTool.Agent/HttpClientDelegatingHandlers/IntervalDelegatingHandler.cs b/src/Ray.BiliBiliTool.Agent/HttpClientDelegatingHandlers/IntervalDelegatingHandler.cs
index 6089d6e7c..952ebac34 100644
--- a/src/Ray.BiliBiliTool.Agent/HttpClientDelegatingHandlers/IntervalDelegatingHandler.cs
+++ b/src/Ray.BiliBiliTool.Agent/HttpClientDelegatingHandlers/IntervalDelegatingHandler.cs
@@ -16,7 +16,9 @@ public class IntervalDelegatingHandler : DelegatingHandler
private readonly SecurityOptions _securityOptions;
private readonly Dictionary _special = new Dictionary() {
- {"/xlive/lottery-interface/v1/Anchor/Join",3 }//天选抽奖,有时效,不能间隔过久,使用默认3秒
+ {"/xlive/lottery-interface/v1/Anchor/Join",3 },//天选抽奖,有时效,不能间隔过久,使用默认3秒
+ {"/xlive/data-interface/v1/x25Kn/E", 1},
+ {"/xlive/data-interface/v1/x25Kn/X", 1},
};
public IntervalDelegatingHandler(IOptionsMonitor securityOptions)
diff --git a/src/Ray.BiliBiliTool.Agent/Ray.BiliBiliTool.Agent.csproj b/src/Ray.BiliBiliTool.Agent/Ray.BiliBiliTool.Agent.csproj
index 42789ea9c..8c3c6d1fd 100644
--- a/src/Ray.BiliBiliTool.Agent/Ray.BiliBiliTool.Agent.csproj
+++ b/src/Ray.BiliBiliTool.Agent/Ray.BiliBiliTool.Agent.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/src/Ray.BiliBiliTool.Application.Contracts/ILiveFansMedalAppService.cs b/src/Ray.BiliBiliTool.Application.Contracts/ILiveFansMedalAppService.cs
new file mode 100644
index 000000000..acf205004
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Application.Contracts/ILiveFansMedalAppService.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel;
+
+namespace Ray.BiliBiliTool.Application.Contracts
+{
+ ///
+ /// 直播粉丝牌任务
+ ///
+ [Description("LiveFansMedal")]
+ public interface ILiveFansMedalAppService : IAppService
+ {
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Application/LiveFansMedalAppService.cs b/src/Ray.BiliBiliTool.Application/LiveFansMedalAppService.cs
new file mode 100644
index 000000000..427d2b8f5
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Application/LiveFansMedalAppService.cs
@@ -0,0 +1,48 @@
+using Microsoft.Extensions.Logging;
+using Ray.BiliBiliTool.Application.Attributes;
+using Ray.BiliBiliTool.Application.Contracts;
+using Ray.BiliBiliTool.DomainService.Interfaces;
+
+namespace Ray.BiliBiliTool.Application
+{
+ public class LiveFansMedalAppService : AppService, ILiveFansMedalAppService
+ {
+ private readonly ILogger _logger;
+ private readonly ILiveDomainService _liveDomainService;
+
+ public LiveFansMedalAppService(
+ ILiveDomainService liveDomainService,
+ ILogger logger
+ )
+ {
+ _liveDomainService = liveDomainService;
+ _logger = logger;
+ }
+
+ [TaskInterceptor("直播间互动", TaskLevel.One)]
+ public override void DoTask()
+ {
+ SendDanmaku();
+ Like();
+ HeartBeat();
+ }
+
+ [TaskInterceptor("发送弹幕", TaskLevel.Two,false)]
+ private void SendDanmaku()
+ {
+ _liveDomainService.SendDanmakuToFansMedalLive();
+ }
+
+ [TaskInterceptor("点赞直播间", TaskLevel.Two,false)]
+ private void Like()
+ {
+ _liveDomainService.LikeFansMedalLive();
+ }
+
+ [TaskInterceptor("直播时长挂机", TaskLevel.Two,false)]
+ private void HeartBeat()
+ {
+ _liveDomainService.SendHeartBeatToFansMedalLive();
+ }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Application/LoginTaskAppService.cs b/src/Ray.BiliBiliTool.Application/LoginTaskAppService.cs
index 81a09764a..e7dd5967a 100644
--- a/src/Ray.BiliBiliTool.Application/LoginTaskAppService.cs
+++ b/src/Ray.BiliBiliTool.Application/LoginTaskAppService.cs
@@ -18,6 +18,8 @@
using Ray.BiliBiliTool.Application.Attributes;
using Ray.BiliBiliTool.Application.Contracts;
using Ray.BiliBiliTool.Infrastructure.Enums;
+using Ray.BiliBiliTool.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
namespace Ray.BiliBiliTool.Application
{
@@ -34,8 +36,7 @@ public LoginTaskAppService(
ILogger logger,
IPassportApi passportApi,
IHostEnvironment hostingEnvironment,
- IQingLongApi qingLongApi
- )
+ IQingLongApi qingLongApi)
{
_configuration = configuration;
_logger = logger;
diff --git a/src/Ray.BiliBiliTool.Application/Ray.BiliBiliTool.Application.csproj b/src/Ray.BiliBiliTool.Application/Ray.BiliBiliTool.Application.csproj
index 65607513b..6ae26800a 100644
--- a/src/Ray.BiliBiliTool.Application/Ray.BiliBiliTool.Application.csproj
+++ b/src/Ray.BiliBiliTool.Application/Ray.BiliBiliTool.Application.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Ray.BiliBiliTool.Config/Extensions/ServiceCollectionExtension.cs b/src/Ray.BiliBiliTool.Config/Extensions/ServiceCollectionExtension.cs
index f62ac0c30..9169b6d06 100644
--- a/src/Ray.BiliBiliTool.Config/Extensions/ServiceCollectionExtension.cs
+++ b/src/Ray.BiliBiliTool.Config/Extensions/ServiceCollectionExtension.cs
@@ -25,6 +25,7 @@ public static IServiceCollection AddBiliBiliConfigs(this IServiceCollection serv
.Configure(configuration.GetSection("UnfollowBatchedTaskConfig"))
.Configure(configuration.GetSection("Security"))
.Configure(configuration.GetSection("ReceiveVipPrivilegeConfig"))
+ .Configure(configuration.GetSection("LiveFansMedalTaskOptions"))
.Configure>(Constants.OptionsNames.ExpDictionaryName, configuration.GetSection("Exp"))
.Configure>(Constants.OptionsNames.DonateCoinCanContinueStatusDictionaryName, configuration.GetSection("DonateCoinCanContinueStatus"));
diff --git a/src/Ray.BiliBiliTool.Config/Options/LiveFansMedalTaskOptions.cs b/src/Ray.BiliBiliTool.Config/Options/LiveFansMedalTaskOptions.cs
new file mode 100644
index 000000000..88abea4c3
--- /dev/null
+++ b/src/Ray.BiliBiliTool.Config/Options/LiveFansMedalTaskOptions.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.Config.Options
+{
+ ///
+ /// 粉丝牌等级任务相关配置
+ ///
+ public class LiveFansMedalTaskOptions
+ {
+ ///
+ /// 自定义发送弹幕内容,如 “打卡” 等来触发直播间内机器人关键词
+ ///
+ public string DanmakuContent { get; set; } = "OvO";
+
+ ///
+ /// 心跳包发送的个数 / 挂机的时间,单位为分钟
+ ///
+ public int HeartBeatNumber { get; set; } = 70;
+
+ ///
+ /// 当心跳包发送连续失败多少次时放弃
+ ///
+ public int HeartBeatSendGiveUpThreshold { get; set; } = 5;
+
+ ///
+ /// 对于直播时长任务是否跳过粉丝牌等级大于等于 20 的
+ ///
+ public bool IsSkipLevel20Medal { get; set; } = true;
+
+ //public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
+
+ public const int HeartBeatInterval = 60;
+ }
+}
diff --git a/src/Ray.BiliBiliTool.Console/Properties/launchSettings.json b/src/Ray.BiliBiliTool.Console/Properties/launchSettings.json
index 7d719996c..bd1534eba 100644
--- a/src/Ray.BiliBiliTool.Console/Properties/launchSettings.json
+++ b/src/Ray.BiliBiliTool.Console/Properties/launchSettings.json
@@ -10,4 +10,4 @@
"commandName": "Docker"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj b/src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj
index cc6d544eb..bee0b6c80 100644
--- a/src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj
+++ b/src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj
@@ -55,12 +55,12 @@
-
-
-
+
+
+
-
-
+
+
@@ -95,5 +95,5 @@
-
+
\ No newline at end of file
diff --git a/src/Ray.BiliBiliTool.Console/appsettings.json b/src/Ray.BiliBiliTool.Console/appsettings.json
index a3f9a2999..9b1e4c3d6 100644
--- a/src/Ray.BiliBiliTool.Console/appsettings.json
+++ b/src/Ray.BiliBiliTool.Console/appsettings.json
@@ -1,7 +1,7 @@
{
//Cookie集合,取自浏览器,必填
"BiliBiliCookies": [ //Cookie字符串集合,登录bilibili后F12获取,形如"_uuid=abcd; buvid3=1234; sid=abc123"
- "",
+ ""
],
"RunTasks": "Daily", //要运行的任务名称[Daily,LiveLottery,UnfollowBatched,VipBigPoint,Test],多个使用&分隔,如“Daily&LiveLottery”,建议使用命令行参数指定
@@ -42,6 +42,14 @@
"Cron": "7 1 * * *"
},
+ "LiveFansMedalTaskConfig": {
+ "Cron": "5 0 * * *",
+ "DanmakuContent": "OvO",
+ "HeartBeatNumber": 70, //直播间观看的时长,单位为分钟",
+ "HeartBeatSendGiveUpThreshold": 5, //当心跳包发送连续失败多少次时放弃
+ "IsSkipLevel20Medal": true // 是否跳过粉丝牌等级 >=0 的
+ },
+
//安全相关配置
"Security": {
"IsSkipDailyTask": false, //是否跳过执行任务,用于特殊情况下,通过配置灵活的开启和关闭任务
diff --git a/src/Ray.BiliBiliTool.DomainService/Dtos/FansMedalInfoDto.cs b/src/Ray.BiliBiliTool.DomainService/Dtos/FansMedalInfoDto.cs
new file mode 100644
index 000000000..f0f66385d
--- /dev/null
+++ b/src/Ray.BiliBiliTool.DomainService/Dtos/FansMedalInfoDto.cs
@@ -0,0 +1,28 @@
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.DomainService.Dtos
+{
+ public class FansMedalInfoDto
+ {
+ public FansMedalInfoDto(int roomId, MedalWallDto medalInfo, GetLiveRoomInfoResponse liveRoomInfo)
+ {
+ this.RoomId = roomId;
+ this.MedalInfo = medalInfo;
+ this.LiveRoomInfo = liveRoomInfo;
+ }
+
+ // 直播间 id
+ public int RoomId { get; set; }
+
+ // 粉丝牌信息
+ public MedalWallDto MedalInfo { get; set; }
+
+ // 直播间信息
+ public GetLiveRoomInfoResponse LiveRoomInfo { get; set; }
+ }
+}
diff --git a/src/Ray.BiliBiliTool.DomainService/Dtos/HeartBeatIterationInfoDto.cs b/src/Ray.BiliBiliTool.DomainService/Dtos/HeartBeatIterationInfoDto.cs
new file mode 100644
index 000000000..e7202eff5
--- /dev/null
+++ b/src/Ray.BiliBiliTool.DomainService/Dtos/HeartBeatIterationInfoDto.cs
@@ -0,0 +1,40 @@
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ray.BiliBiliTool.DomainService.Dtos
+{
+ public class HeartBeatIterationInfoDto
+ {
+ public HeartBeatIterationInfoDto(
+ int roomId,
+ GetLiveRoomInfoResponse roomInfo,
+ HeartBeatResponse heartBeatInfo,
+ int heartBeatCount,
+ long lastBeatTime)
+ {
+ RoomId = roomId;
+ RoomInfo = roomInfo;
+ HeartBeatInfo = heartBeatInfo;
+ HeartBeatCount = heartBeatCount;
+ LastBeatTime = lastBeatTime;
+ }
+
+ public int RoomId { get; set; } = 0;
+
+ public GetLiveRoomInfoResponse RoomInfo { get; set; } = new();
+
+ public HeartBeatResponse HeartBeatInfo { get; set; } = new();
+
+ // 成功发送的心跳包个数
+ public int HeartBeatCount { get; set; } = 0;
+
+ public long LastBeatTime { get; set; } = 0;
+
+ // 连续失败的次数
+ public int FailedTimes { get; set; } = 0;
+ }
+}
diff --git a/src/Ray.BiliBiliTool.DomainService/Interfaces/ILiveDomainService.cs b/src/Ray.BiliBiliTool.DomainService/Interfaces/ILiveDomainService.cs
index 1355e4eb2..f7d724de9 100644
--- a/src/Ray.BiliBiliTool.DomainService/Interfaces/ILiveDomainService.cs
+++ b/src/Ray.BiliBiliTool.DomainService/Interfaces/ILiveDomainService.cs
@@ -29,5 +29,20 @@ public interface ILiveDomainService : IDomainService
void TryJoinTianXuan(ListItemDto target);
void GroupFollowing();
+
+ ///
+ /// 发送弹幕
+ ///
+ void SendDanmakuToFansMedalLive();
+
+ ///
+ /// 直播时长挂机
+ ///
+ void SendHeartBeatToFansMedalLive();
+
+ ///
+ /// 点赞直播间
+ ///
+ void LikeFansMedalLive();
}
}
diff --git a/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs b/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs
index cd790d7e5..6c7214b3f 100644
--- a/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs
+++ b/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs
@@ -1,16 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Text;
+using System.Threading;
+using System.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Relation;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces;
using Ray.BiliBiliTool.Config.Options;
+using Ray.BiliBiliTool.DomainService.Dtos;
using Ray.BiliBiliTool.DomainService.Interfaces;
+using Ray.BiliBiliTool.Infrastructure;
namespace Ray.BiliBiliTool.DomainService
{
@@ -22,23 +28,37 @@ public class LiveDomainService : ILiveDomainService
private readonly ILogger _logger;
private readonly ILiveApi _liveApi;
private readonly IRelationApi _relationApi;
+ private readonly ILiveTraceApi _liveTraceApi;
+ private readonly IUserInfoApi _userInfoApi;
private readonly LiveLotteryTaskOptions _liveLotteryTaskOptions;
- private readonly BiliCookie _biliCookie;
+ private readonly LiveFansMedalTaskOptions _liveFansMedalTaskOptions;
private readonly DailyTaskOptions _dailyTaskOptions;
+ private readonly SecurityOptions _securityOptions;
+ private readonly BiliCookie _biliCookie;
+
public LiveDomainService(ILogger logger,
ILiveApi liveApi,
IRelationApi relationApi,
+ ILiveTraceApi liveTraceApi,
+ IUserInfoApi userInfoApi,
IOptionsMonitor dailyTaskOptions,
IOptionsMonitor liveLotteryTaskOptions,
+ IOptionsMonitor liveFansMedalTaskOptions,
+ IOptionsMonitor securityOptions,
BiliCookie biliCookie)
{
_logger = logger;
_liveApi = liveApi;
_relationApi = relationApi;
+ _liveTraceApi = liveTraceApi;
+ _userInfoApi = userInfoApi;
_liveLotteryTaskOptions = liveLotteryTaskOptions.CurrentValue;
- _biliCookie = biliCookie;
_dailyTaskOptions = dailyTaskOptions.CurrentValue;
+ _liveFansMedalTaskOptions = liveFansMedalTaskOptions.CurrentValue;
+ _securityOptions = securityOptions.CurrentValue;
+ _biliCookie = biliCookie;
+
}
///
@@ -385,5 +405,264 @@ private long GetOrCreateTianXuanGroupId()
return groupId;
}
#endregion
+
+ public void SendDanmakuToFansMedalLive()
+ {
+ if (!CheckLiveCookie()) return;
+
+ GetFansMedalInfoList().ForEach(info =>
+ {
+ var medal = info.MedalInfo;
+
+ _logger.LogInformation("【直播间】{liveRoomName}", medal.Target_name);
+ _logger.LogInformation("【粉丝牌】{medalName}", medal.Medal_info.Medal_name);
+
+ _logger.LogInformation("正在发送弹幕...");
+
+ // 通过空间主页信息获取直播间 id
+ int liveHostUserId = medal.Medal_info.Target_id;
+ var spaceInfo = _userInfoApi.GetSpaceInfo(liveHostUserId).Result;
+ if (spaceInfo.Code != 0)
+ {
+ _logger.LogError("【获取直播间信息】失败");
+ _logger.LogError("【原因】{message}", spaceInfo.Message);
+ return;
+ }
+
+ // 发送弹幕
+ var sendResult = _liveApi.SendLiveDanmuku(new SendLiveDanmukuRequest(
+ _biliCookie.BiliJct,
+ spaceInfo.Data.Live_room.Roomid,
+ _liveFansMedalTaskOptions.DanmakuContent)).Result;
+
+ if (sendResult.Code != 0)
+ {
+ _logger.LogError("【弹幕发送】失败");
+ _logger.LogError("【原因】{message}", sendResult.Message);
+ return;
+ }
+
+ _logger.LogInformation("【弹幕发送】成功~,你和主播 {name} 的亲密值增加了100!", spaceInfo.Data.Name);
+ });
+ }
+
+ public void SendHeartBeatToFansMedalLive()
+ {
+ if (!CheckLiveCookie()) return;
+
+ var infoList = new List();
+ GetFansMedalInfoList().ForEach((medal) => infoList.Add(new(medal.RoomId, medal.LiveRoomInfo, new(), 0, 0)));
+
+ if (infoList.Count == 0)
+ {
+ _logger.LogInformation("【直播观看时长】跳过,未检测到符合条件的主播");
+ return;
+ }
+
+ var Now = () => new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
+
+ while (infoList.Min(
+ info => info.FailedTimes >= _liveFansMedalTaskOptions.HeartBeatSendGiveUpThreshold
+ ? int.MaxValue :
+ info.HeartBeatCount)
+ < _liveFansMedalTaskOptions.HeartBeatNumber)
+ {
+ foreach (var info in infoList)
+ {
+ // 忽略连续失败超过上限的直播间
+ if (info.FailedTimes >= _liveFansMedalTaskOptions.HeartBeatSendGiveUpThreshold) continue;
+
+ string uuid = Guid.NewGuid().ToString();
+ var current = Now();
+ if (current - info.LastBeatTime <= (LiveFansMedalTaskOptions.HeartBeatInterval + 5) * 1000)
+ {
+ int sleepTime = (int)((LiveFansMedalTaskOptions.HeartBeatInterval + 5) * 1000 - (current - info.LastBeatTime));
+ _logger.LogDebug("【休眠】{time} 毫秒", sleepTime);
+ Thread.Sleep(sleepTime);
+ }
+
+ // Heart Beat 接口
+ var timestamp = Now();
+ BiliApiResponse heartBeatResult = null;
+ if (info.HeartBeatCount == 0)
+ {
+ heartBeatResult = _liveTraceApi.EnterRoom(
+ new EnterRoomRequest(
+ info.RoomId,
+ info.RoomInfo.Parent_area_id,
+ info.RoomInfo.Area_id,
+ info.HeartBeatCount,
+ timestamp,
+ _securityOptions.UserAgent,
+ _biliCookie.BiliJct,
+ info.RoomInfo.Uid,
+ $"[\"{_biliCookie.LiveBuvid}\",\"{uuid}\"]")
+ )
+ .Result;
+ }
+ else
+ {
+ heartBeatResult = _liveTraceApi.HeartBeat(
+ new HeartBeatRequest(
+ info.RoomId,
+ info.RoomInfo.Parent_area_id,
+ info.RoomInfo.Area_id,
+ info.HeartBeatCount,
+ _biliCookie.LiveBuvid,
+ timestamp,
+ info.HeartBeatInfo.Timestamp,
+ _securityOptions.UserAgent,
+ info.HeartBeatInfo.Secret_rule,
+ info.HeartBeatInfo.Secret_key,
+ _biliCookie.BiliJct,
+ uuid,
+ $"[\"{_biliCookie.LiveBuvid}\",\"{uuid}\"]")
+ )
+ .Result;
+ }
+
+ info.LastBeatTime = Now();
+
+ if (heartBeatResult != null && heartBeatResult.Data != null)
+ {
+ info.HeartBeatInfo.Secret_key = heartBeatResult.Data.Secret_key;
+ info.HeartBeatInfo.Secret_rule = heartBeatResult.Data.Secret_rule;
+ info.HeartBeatInfo.Timestamp = heartBeatResult.Data.Timestamp;
+ }
+
+ if (heartBeatResult == null || heartBeatResult.Code != 0)
+ {
+ _logger.LogError("【心跳包】直播间 {room} 发送失败", info.RoomId);
+ _logger.LogError("【原因】{message}", heartBeatResult != null ? heartBeatResult.Message : "");
+ info.FailedTimes += 1;
+ continue;
+ }
+
+ info.HeartBeatCount += 1;
+ info.FailedTimes = 0;
+
+ _logger.LogInformation("【直播间】{roomId} 的第 {index} 个心跳包发送成功", info.RoomId, info.HeartBeatCount);
+ }
+ }
+
+ var successCount = infoList.Count(info => info.HeartBeatCount >= _liveFansMedalTaskOptions.HeartBeatNumber);
+ _logger.LogInformation("【直播观看时长】完成情况:{success}/{total} ", successCount, infoList.Count);
+ }
+
+ public void LikeFansMedalLive()
+ {
+ if (!CheckLiveCookie()) return;
+
+ GetFansMedalInfoList().ForEach(info =>
+ {
+ var result = _liveApi.LikeLiveRoom(new LikeLiveRoomRequest(info.RoomId, _biliCookie.BiliJct)).Result;
+ if (result.Code == 0)
+ {
+ _logger.LogInformation("【点赞直播间】{roomId} 完成", info.RoomId);
+ }
+ else
+ {
+ _logger.LogError("【点赞直播间】{roomId} 时候出现错误", info.RoomId);
+ _logger.LogError("【原因】{message}", result.Message);
+ }
+ });
+ }
+
+ private List GetFansMedalInfoList()
+ {
+ _logger.LogInformation("【获取直播列表】获取拥有粉丝牌的直播列表");
+ var medalWallInfo = this._liveApi.GetMedalWall(int.Parse(this._biliCookie.UserId)).Result;
+
+ if (medalWallInfo.Code != 0)
+ {
+ _logger.LogError("【获取直播列表】失败");
+ _logger.LogError("【原因】{message}", medalWallInfo.Message);
+ return null;
+ }
+
+ var infoList = new List();
+ foreach (var medal in medalWallInfo.Data.List)
+ {
+ _logger.LogInformation("【主播】{name} ", medal.Target_name);
+ if (_liveFansMedalTaskOptions.IsSkipLevel20Medal && medal.Medal_info.Level >= 20)
+ {
+ _logger.LogInformation("粉丝牌等级为 {level},观看将不再增长亲密度,跳过", medal.Medal_info.Level);
+ continue;
+ }
+
+ // 通过空间主页信息获取直播间 id
+ int liveHostUserId = medal.Medal_info.Target_id;
+ var spaceInfo = _userInfoApi.GetSpaceInfo(liveHostUserId).Result;
+ if (spaceInfo.Code != 0)
+ {
+ _logger.LogError("【获取空间信息】失败");
+ _logger.LogError("【原因】{message}", spaceInfo.Message);
+ continue;
+ }
+
+ var roomId = spaceInfo.Data.Live_room.Roomid;
+
+ // 获取直播间详细信息
+ var liveRoomInfo = _liveApi.GetLiveRoomInfo(roomId).Result;
+ if (liveRoomInfo.Code != 0)
+ {
+ _logger.LogError("【获取直播间信息】失败");
+ _logger.LogError("【原因】{message}", liveRoomInfo.Message);
+ continue;
+ }
+
+ infoList.Add(new FansMedalInfoDto(roomId, medal, liveRoomInfo.Data));
+ }
+
+ return infoList;
+ }
+
+ ///
+ /// 自动配置直播相关 Cookie,来兼容较低版本中保存的 Cookie 配置
+ ///
+ ///
+ /// bool 成功配置 or not
+ ///
+ private bool CheckLiveCookie()
+ {
+ // 检测 _biliCookie 是否正确配置
+ if (string.IsNullOrWhiteSpace(_biliCookie.LiveBuvid))
+ {
+ try
+ {
+ _logger.LogInformation("检测到直播 Cookie 未正确配置,尝试自动配置中...");
+
+ // 请求主播主页来正确配置 cookie
+ var liveHome = _liveApi.GetLiveHome().Result;
+ var liveHomeContent = JsonConvert.DeserializeObject(liveHome.Content.ReadAsStringAsync().Result);
+ if (liveHomeContent.Code != 0)
+ {
+ throw new Exception(liveHomeContent.Message);
+ }
+
+ IEnumerable liveCookies = liveHome.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value;
+ var ckItemList = new List();
+ foreach (var item in liveCookies)
+ {
+ ckItemList.Add(item.Split(';').FirstOrDefault());
+ }
+ _biliCookie.LiveBuvid = CookieInfo.BuildCookieItemDictionaryByCookieItemList(
+ ckItemList,
+ null,
+ v => v.Contains(',') ? Uri.EscapeDataString(v) : v)
+ [_biliCookie.GetType().GetPropertyDescription(nameof(BiliCookie.LiveBuvid))];
+
+ _logger.LogDebug("LiveBuvid {value}", _biliCookie.LiveBuvid);
+ _logger.LogInformation("直播 Cookie 配置成功!");
+ }
+ catch (Exception exception)
+ {
+ _logger.LogError("【配置直播Cookie】失败,放弃执行后续任务...");
+ _logger.LogError("【原因】{message}", exception.Message);
+ return false;
+ }
+ }
+ return true;
+ }
}
}
diff --git a/src/Ray.BiliBiliTool.DomainService/Ray.BiliBiliTool.DomainService.csproj b/src/Ray.BiliBiliTool.DomainService/Ray.BiliBiliTool.DomainService.csproj
index 675e9aafd..560a330c7 100644
--- a/src/Ray.BiliBiliTool.DomainService/Ray.BiliBiliTool.DomainService.csproj
+++ b/src/Ray.BiliBiliTool.DomainService/Ray.BiliBiliTool.DomainService.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/src/Ray.BiliBiliTool.Infrastructure/Cookie/CookieInfo.cs b/src/Ray.BiliBiliTool.Infrastructure/Cookie/CookieInfo.cs
index f26bbb625..2abc29a57 100644
--- a/src/Ray.BiliBiliTool.Infrastructure/Cookie/CookieInfo.cs
+++ b/src/Ray.BiliBiliTool.Infrastructure/Cookie/CookieInfo.cs
@@ -29,7 +29,7 @@ public virtual void Check()
if (string.IsNullOrWhiteSpace(CookieStr)) throw new Exception("Cookie字符串为空");
}
- private static Dictionary BuildCookieItemDictionaryByCookieItemList(IEnumerable cookieItemList, Func nameBuilder = null, Func valueBuilder = null)
+ public static Dictionary BuildCookieItemDictionaryByCookieItemList(IEnumerable cookieItemList, Func nameBuilder = null, Func valueBuilder = null)
{
var re = new Dictionary();
foreach (var item in cookieItemList ?? new List())
diff --git a/src/Ray.BiliBiliTool.Infrastructure/Ray.BiliBiliTool.Infrastructure.csproj b/src/Ray.BiliBiliTool.Infrastructure/Ray.BiliBiliTool.Infrastructure.csproj
index ea1bccbe6..25976c9a6 100644
--- a/src/Ray.BiliBiliTool.Infrastructure/Ray.BiliBiliTool.Infrastructure.csproj
+++ b/src/Ray.BiliBiliTool.Infrastructure/Ray.BiliBiliTool.Infrastructure.csproj
@@ -8,6 +8,6 @@
-
+
\ No newline at end of file
diff --git a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.Batched/Ray.Serilog.Sinks.Batched.csproj b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.Batched/Ray.Serilog.Sinks.Batched.csproj
index 6aa4c6ea2..a08684877 100644
--- a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.Batched/Ray.Serilog.Sinks.Batched.csproj
+++ b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.Batched/Ray.Serilog.Sinks.Batched.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.ServerChanBatched/Ray.Serilog.Sinks.ServerChanBatched.csproj b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.ServerChanBatched/Ray.Serilog.Sinks.ServerChanBatched.csproj
index 837a219a8..f945703ea 100644
--- a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.ServerChanBatched/Ray.Serilog.Sinks.ServerChanBatched.csproj
+++ b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.ServerChanBatched/Ray.Serilog.Sinks.ServerChanBatched.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.TelegramBatched/Ray.Serilog.Sinks.TelegramBatched.csproj b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.TelegramBatched/Ray.Serilog.Sinks.TelegramBatched.csproj
index f970b9bf2..b9a3aa956 100644
--- a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.TelegramBatched/Ray.Serilog.Sinks.TelegramBatched.csproj
+++ b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.TelegramBatched/Ray.Serilog.Sinks.TelegramBatched.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.WorkWeiXinBatched/Ray.Serilog.Sinks.WorkWeiXinBatched.csproj b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.WorkWeiXinBatched/Ray.Serilog.Sinks.WorkWeiXinBatched.csproj
index 837a219a8..f945703ea 100644
--- a/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.WorkWeiXinBatched/Ray.Serilog.Sinks.WorkWeiXinBatched.csproj
+++ b/src/Ray.Serilog.Sinks/Ray.Serilog.Sinks.WorkWeiXinBatched/Ray.Serilog.Sinks.WorkWeiXinBatched.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/test/BiliAgentTest/BiliAgentTest.csproj b/test/BiliAgentTest/BiliAgentTest.csproj
index 0a8272606..5f8cefe40 100644
--- a/test/BiliAgentTest/BiliAgentTest.csproj
+++ b/test/BiliAgentTest/BiliAgentTest.csproj
@@ -8,13 +8,13 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/test/BiliAgentTest/LiveApiTest.cs b/test/BiliAgentTest/LiveApiTest.cs
index f600c2643..48acde41b 100644
--- a/test/BiliAgentTest/LiveApiTest.cs
+++ b/test/BiliAgentTest/LiveApiTest.cs
@@ -1,4 +1,4 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
@@ -16,7 +16,7 @@ public class LiveApiTest
{
public LiveApiTest()
{
- Program.CreateHost(new[] { "--ENVIRONMENT=Development" });//ĬPrdָΪDevԶȡû
+ Program.CreateHost(new[] { "--ENVIRONMENT=Development" });//ĬÈÏPrd»·¾³£¬ÕâÀïÖ¸¶¨ÎªDevºó£¬¿ÉÒÔ¶ÁÈ¡µ½Óû§»úÃÜÅäÖÃ
}
[Fact]
@@ -82,5 +82,79 @@ public void GetLiveWalletStatus_Normal_Success()
Assert.False(re.Code != 0);
}
}
+
+ [Fact]
+ public void GetMedalWall_Normal_Success()
+ {
+ using var scope = Global.ServiceProviderRoot.CreateScope();
+
+ var ck = scope.ServiceProvider.GetRequiredService();
+ var api = scope.ServiceProvider.GetRequiredService();
+
+ BiliApiResponse re = api.GetMedalWall(919174).Result;
+
+ Assert.NotEmpty(re.Data.List);
+
+ var md = re.Data.List[0];
+ Assert.NotNull(md);
+ Assert.False(String.IsNullOrEmpty(md.Link));
+ Assert.False(String.IsNullOrEmpty(md.Target_name));
+ Assert.NotNull(md.Medal_info);
+ Assert.False(String.IsNullOrEmpty(md.Medal_info.Medal_name));
+ Assert.True(md.Medal_info.Medal_id > 0);
+ }
+
+ [Fact]
+ public void WearMedalWall_Normal_Success()
+ {
+ using var scope = Global.ServiceProviderRoot.CreateScope();
+
+ var ck = scope.ServiceProvider.GetRequiredService();
+ var api = scope.ServiceProvider.GetRequiredService();
+ var biliCookie = scope.ServiceProvider.GetRequiredService();
+
+ // 猫雷粉丝牌
+ var request = new WearMedalWallRequest(biliCookie.BiliJct, 365421);
+
+ BiliApiResponse re = api.WearMedalWall(request).Result;
+
+ Assert.True(re.Code == 0);
+ }
+
+ [Fact]
+ public void GetSpaceInfo_Normal_Success()
+ {
+ using var scope = Global.ServiceProviderRoot.CreateScope();
+
+ var ck = scope.ServiceProvider.GetRequiredService();
+ var api = scope.ServiceProvider.GetRequiredService();
+ var biliCookie = scope.ServiceProvider.GetRequiredService();
+
+ BiliApiResponse re = api.GetSpaceInfo(919174).Result;
+
+ Assert.True(re.Code == 0);
+ Assert.NotNull(re.Data);
+ Assert.Equal(919174, re.Data.Mid);
+ Assert.NotNull(re.Data.Live_room);
+ Assert.Equal(3115258, re.Data.Live_room.Roomid);
+ Assert.False(String.IsNullOrEmpty(re.Data.Name));
+ Assert.False(String.IsNullOrEmpty(re.Data.Live_room.Title));
+ }
+
+ [Fact]
+ public void SendLiveDanmuku_Normal_Success()
+ {
+ using var scope = Global.ServiceProviderRoot.CreateScope();
+
+ var ck = scope.ServiceProvider.GetRequiredService();
+ var api = scope.ServiceProvider.GetRequiredService();
+ var biliCookie = scope.ServiceProvider.GetRequiredService();
+
+ var request = new SendLiveDanmukuRequest(biliCookie.BiliJct, 63666, "63666");
+
+ BiliApiResponse re = api.SendLiveDanmuku(request).Result;
+
+ Assert.True(re.Code == 0);
+ }
}
}
diff --git a/test/BiliAgentTest/LiveTraceApiTest.cs b/test/BiliAgentTest/LiveTraceApiTest.cs
new file mode 100644
index 000000000..32332957b
--- /dev/null
+++ b/test/BiliAgentTest/LiveTraceApiTest.cs
@@ -0,0 +1,36 @@
+using Microsoft.Extensions.DependencyInjection;
+using Ray.BiliBiliTool.Agent;
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Live;
+using Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces;
+using Ray.BiliBiliTool.Console;
+using Ray.BiliBiliTool.Infrastructure;
+using Xunit;
+
+namespace BiliAgentTest
+{
+ public class LiveTraceApiTest
+ {
+ public LiveTraceApiTest()
+ {
+ Program.CreateHost(new[] { "--ENVIRONMENT=Development" });
+ }
+
+ [Fact]
+ public void WebHeartBeat_Normal_Success()
+ {
+ using var scope = Global.ServiceProviderRoot.CreateScope();
+
+ var ck = scope.ServiceProvider.GetRequiredService();
+ var api = scope.ServiceProvider.GetRequiredService();
+
+ var request = new WebHeartBeatRequest(63666, 60);
+
+ var re = api.WebHeartBeat(request).Result;
+
+ Assert.Equal(0, re.Code);
+ Assert.Equal("0", re.Message);
+ Assert.Equal(60, re.Data.Next_interval);
+ }
+ }
+}
diff --git a/test/ConfigTest/ConfigTest.csproj b/test/ConfigTest/ConfigTest.csproj
index 59cead1c9..2355e2c7b 100644
--- a/test/ConfigTest/ConfigTest.csproj
+++ b/test/ConfigTest/ConfigTest.csproj
@@ -6,13 +6,13 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/test/LogTest/LogTest.csproj b/test/LogTest/LogTest.csproj
index 6c443a99b..4be2b3b3a 100644
--- a/test/LogTest/LogTest.csproj
+++ b/test/LogTest/LogTest.csproj
@@ -7,13 +7,13 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive