Skip to content

Commit

Permalink
修复错误,并增加专栏投币功能与领取大会员经验的功能 (#617)
Browse files Browse the repository at this point in the history
* 添加条件排除无法进行直播挂机的up主

* 为跳过的主播添加跳过提示

* 专栏投币基本框架已完成

* 专栏签到全流程基本完成,将开发环境硬编码调整为专栏投币模式开始进行挂机测试。

* 修复了一下Bug,目前专栏投币功能已经能正常运行

* 显式添加个性化配置IsDonateCoinForArticle

* 添加对专栏投币同时点赞的功能,与视频点赞共享一个参数

* 修复硬币数量多的时候可投硬币数返回值错误

* 添加了一些log,方便调试

* 修改单词拼写错误

* 临时的windows环境构建脚本

* 删除未使用的参数

* 添加卡券状态查询相关api

* 完成每日领取大会员经验任务,同时修复在大积分任务中的一些错误

* 修改调用错误
Polaris-cn10086 authored Dec 1, 2023
1 parent 0f21718 commit aa73789
Showing 22 changed files with 818 additions and 11 deletions.
16 changes: 16 additions & 0 deletions scripts/publish.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime win-x86 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/win-x86
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime win-x64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/win-x64
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime win-arm64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/win-arm64
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime linux-x64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/linux-x64
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime linux-musl-x64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/linux-musl-x64
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime linux-arm64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/linux-arm64
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime linux-arm --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/linux-arm
dotnet.exe publish ../src/Ray.BiliBiliTool.Console/Ray.BiliBiliTool.Console.csproj --runtime osx-x64 --no-self-contained -c Release -p:PublishSingleFile=true -o ./bin/Publish/osx-x64
Remove-Item ./bin/Publish/win-x86/*.pdb
Remove-Item ./bin/Publish/win-x64/*.pdb
Remove-Item ./bin/Publish/win-arm64/*.pdb
Remove-Item ./bin/Publish/linux-x64/*.pdb
Remove-Item ./bin/Publish/linux-musl-x64/*.pdb
Remove-Item ./bin/Publish/linux-arm64/*.pdb
Remove-Item ./bin/Publish/linux-arm/*.pdb
Remove-Item ./bin/Publish/osx-x64/*.pdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;

public class AddCoinForArticleRequest
{
public AddCoinForArticleRequest(long cvid,long mid,string csrf)
{
Aid = cvid;
Upid = mid;
Csrf = csrf;
}

public long Aid { get; set; }

public long Upid { get; set; }

public int Multiply { get; set; } = 1;

// 必须为2
public int Avtype { get; private set; } = 2;

public string Csrf { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;

public class SearchArticleInfoResponse
{
public int Like { get; set; }

public int Coin { get; set; }

public long Mid { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;

public class SearchArticlesByUpIdDto
{
public long Mid { get; set; }

public int Pn { get; set; } = 1;

public int Ps { get; set; } = 30;

public string Sort { get; set; } = "publish_time";
}

public class SearchArticlesByUpIdFullDto : SearchArticlesByUpIdDto
{
public string w_rid { get; set; }

public long wts { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;

namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;

public class SearchUpArticlesResponse
{
public List<ArticleInfo> Articles { get; set; }
public int Count { get; set; }

}

public class ArticleInfo
{
public long Id { get; set; }

public string Title { get; set; }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.VipTask;

public class VipExperienceRequest
{
public string csrf { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections.Generic;

namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.VipTask;

public class VouchersInfoResponse
{
public List<List> List { get; set; }
public bool IsShortVip { get; set; }
public bool IsFreightOpen { get; set; }
public int Level { get; set; }
public int CurExp { get; set; }
public int NextExp { get; set; }
public bool IsVip { get; set; }
public int IsSeniorMember { get; set; }
public int Format060102 { get; set; }
}


public class List
{
public int Type { get; set; }
public int State { get; set; }
public int ExpireTime { get; set; }
public int VipType { get; set; }
public int NextReceiveDays { get; set; }
public int PeriodEndUnix { get; set; }
}

45 changes: 45 additions & 0 deletions src/Ray.BiliBiliTool.Agent/BiliBiliAgent/Interfaces/IArticleApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Video;
using WebApiClientCore.Attributes;

namespace Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces
{

[Header("Host", "api.bilibili.com")]
public interface IArticleApi : IBiliBiliApi
{
[Header("Content-Type", "application/x-www-form-urlencoded")]
[Header("Origin", "https://www.bilibili.com")]
[HttpPost("/x/web-interface/coin/add")]
Task<BiliApiResponse> AddCoinForArticle([FormContent] AddCoinForArticleRequest request, [Header("referer")] string refer = "https://www.bilibili.com/read/cv5806746/?from=search&spm_id_from=333.337.0.0");


[Header("Referer", "https://www.bilibili.com/")]
[Header("Origin", "https://space.bilibili.com")]
[HttpGet("/x/space/wbi/article")]
Task<BiliApiResponse<SearchUpArticlesResponse>> SearchUpArticlesByUpId(
[PathQuery] SearchArticlesByUpIdFullDto request);

/// <summary>
/// 获取专栏详情
/// </summary>
/// <param name="cvid"></param>
/// <returns></returns>
[HttpGet("/x/article/viewinfo?id={cvid}")]
Task<BiliApiResponse<SearchArticleInfoResponse>> SearchArticleInfo(long cvid);


[Header("Content-Type", "application/x-www-form-urlencoded")]
[Header("Referer", "https://www.bilibili.com/read/cv{cvid}/?from=search&spm_id_from=333.337.0.0")]
[Header("Origin", "https://www.bilibili.com")]
[HttpPost("/x/article/like?id={cvid}&type=1&csrf={csrf}")]
Task<BiliApiResponse> Like(long cvid, string csrf);

}


}
Original file line number Diff line number Diff line change
@@ -31,5 +31,12 @@ public interface IVipBigPointApi

[HttpPost("/pgc/activity/deliver/task/complete")]
Task<BiliApiResponse> ViewComplete([FormContent] ViewRequest request);

[HttpGet("/x/vip/privilege/my")]
Task<BiliApiResponse<VouchersInfoResponse>> GetVouchersInfo();

[Header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")]
[HttpPost("/x/vip/experience/add")]
Task<BiliApiResponse> GetVipExperience([FormContent] VipExperienceRequest request);
}
}
Original file line number Diff line number Diff line change
@@ -73,6 +73,9 @@ public static IServiceCollection AddBiliBiliClientApi(this IServiceCollection se
services.AddBiliBiliClientApi<ILiveTraceApi>("https://live-trace.bilibili.com");
services.AddBiliBiliClientApi<IHomeApi>("https://www.bilibili.com", false);

// 添加注入
services.AddBiliBiliClientApi<IArticleApi>("https://api.bilibili.com");

//qinglong
var qinglongHost = configuration["QL_URL"] ?? "http://localhost:5600";
services
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading.Tasks;

namespace Ray.BiliBiliTool.Application.Contracts
{
@@ -12,5 +13,6 @@ namespace Ray.BiliBiliTool.Application.Contracts

public interface IVipBigPointAppService : IAppService
{
Task VipExpress();
}
}
30 changes: 26 additions & 4 deletions src/Ray.BiliBiliTool.Application/DailyTaskAppService.cs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ public class DailyTaskAppService : AppService, IDailyTaskAppService
private readonly ILogger<DailyTaskAppService> _logger;
private readonly IAccountDomainService _accountDomainService;
private readonly IVideoDomainService _videoDomainService;
private readonly IArticleDomainService _articleDomainService;
private readonly IDonateCoinDomainService _donateCoinDomainService;
private readonly IMangaDomainService _mangaDomainService;
private readonly ILiveDomainService _liveDomainService;
@@ -41,6 +42,7 @@ public DailyTaskAppService(
IOptionsMonitor<Dictionary<string, int>> dicOptions,
IAccountDomainService accountDomainService,
IVideoDomainService videoDomainService,
IArticleDomainService articleDomainService,
IDonateCoinDomainService donateCoinDomainService,
IMangaDomainService mangaDomainService,
ILiveDomainService liveDomainService,
@@ -56,6 +58,7 @@ public DailyTaskAppService(
_expDic = dicOptions.Get(Constants.OptionsNames.ExpDictionaryName);
_accountDomainService = accountDomainService;
_videoDomainService = videoDomainService;
_articleDomainService = articleDomainService;
_donateCoinDomainService = donateCoinDomainService;
_mangaDomainService = mangaDomainService;
_liveDomainService = liveDomainService;
@@ -79,7 +82,8 @@ public override async Task DoTaskAsync(CancellationToken cancellationToken)

DailyTaskInfo dailyTaskInfo = await GetDailyTaskStatus();
await WatchAndShareVideo(dailyTaskInfo);
await AddCoinsForVideo(userInfo);

await AddCoins(userInfo);

//签到:
await LiveSign();
@@ -91,6 +95,9 @@ public override async Task DoTaskAsync(CancellationToken cancellationToken)
await ReceiveVipPrivilege(userInfo);
await ReceiveMangaVipReward(userInfo);

//TODO 大会员领经验


await Charge(userInfo);
}

@@ -124,7 +131,7 @@ protected async Task<BiliCookie> SetCookiesAsync(BiliCookie biliCookie, Cancella
private async Task<UserInfo> Login()
{
UserInfo userInfo = await _accountDomainService.LoginByCookie();
if (userInfo == null) throw new Exception("登录失败,请检查Cookie");//终止流程
if (userInfo == null) throw new Exception("登录失败,请检查Cookie"); //终止流程

_expDic.TryGetValue("每日登录", out int exp);
_logger.LogInformation("登录成功,经验+{exp} √", exp);
@@ -153,21 +160,36 @@ private async Task WatchAndShareVideo(DailyTaskInfo dailyTaskInfo)
_logger.LogInformation("已配置为关闭,跳过任务");
return;
}

await _videoDomainService.WatchAndShareVideo(dailyTaskInfo);
}

/// <summary>
/// 投币任务
/// </summary>
[TaskInterceptor("投币", rethrowWhenException: false)]
private async Task AddCoinsForVideo(UserInfo userInfo)
private async Task AddCoins(UserInfo userInfo)
{
if (_dailyTaskOptions.SaveCoinsWhenLv6 && userInfo.Level_info.Current_level >= 6)
{
_logger.LogInformation("已经为LV6大佬,开始白嫖");
return;
}
await _donateCoinDomainService.AddCoinsForVideos();

if (_dailyTaskOptions.IsDonateCoinForArticle)
{
_logger.LogInformation("专栏投币已开启");

if (!await _articleDomainService.AddCoinForArticles())
{
_logger.LogInformation("专栏投币结束,转入视频投币");
await _donateCoinDomainService.AddCoinsForVideos();
}
}
else
{
await _donateCoinDomainService.AddCoinsForVideos();
}
}

/// <summary>
71 changes: 66 additions & 5 deletions src/Ray.BiliBiliTool.Application/VipBigPointAppService.cs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.VipTask;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces;
@@ -19,23 +20,81 @@ public class VipBigPointAppService : AppService, IVipBigPointAppService
private readonly IConfiguration _configuration;
private readonly IVipBigPointApi _vipApi;
private readonly IAccountDomainService _loginDomainService;
private readonly IVideoDomainService _videoDomainService;
private readonly IAccountDomainService _accountDomainService;
private readonly BiliCookie _biliCookie;

public VipBigPointAppService(
IConfiguration configuration,
ILogger<VipBigPointAppService> logger,
IVipBigPointApi vipApi,
IAccountDomainService loginDomainService
)
IAccountDomainService loginDomainService,
IVideoDomainService videoDomainService,
BiliCookie biliCookie, IAccountDomainService accountDomainService)
{
_configuration = configuration;
_logger = logger;
_vipApi = vipApi;
_loginDomainService = loginDomainService;
_videoDomainService = videoDomainService;
_biliCookie = biliCookie;
_accountDomainService = accountDomainService;
}

public async Task VipExpress()
{
_logger.LogInformation("大会员经验领取任务开始");
var re = await _vipApi.GetVouchersInfo();
if (re.Code == 0)
{
var state = re.Data.List.Find(x => x.Type == 9).State;

switch (state)
{
case 2:
_logger.LogInformation("大会员经验观看任务未完成");
_logger.LogInformation("开始观看视频");
// 观看视频,暂时没有好办法解决,先这样使着
DailyTaskInfo dailyTaskInfo = await _accountDomainService.GetDailyTaskStatus();
await _videoDomainService.WatchAndShareVideo(dailyTaskInfo);
// 跳转到未兑换,执行兑换任务
goto case 0;

case 1:
_logger.LogInformation("大会员经验已兑换");
break;

case 0:
_logger.LogInformation("大会员经验未兑换");
//兑换api
var response = await _vipApi.GetVipExperience(new VipExperienceRequest()
{
csrf = _biliCookie.BiliJct
});
if (response.Code != 0)
{
_logger.LogInformation("大会员经验领取失败,错误信息:{message}", response.Message);
break;
}
_logger.LogInformation("领取成功,经验+10 √");
break;

default:
_logger.LogDebug("大会员经验领取失败,未知错误");
break;
}

}

}


[TaskInterceptor("大会员大积分", TaskLevel.One)]
public override async Task DoTaskAsync(CancellationToken cancellationToken)
{
await VipExpress();

// TODO 解决taskInfo在一个任务出错后,后续的任务均会报空引用错误
var ui = await GetUserInfo();

if (ui.GetVipType() == VipType.None)
@@ -66,7 +125,7 @@ public override async Task DoTaskAsync(CancellationToken cancellationToken)
taskInfo = await ViewAnimate(taskInfo);

//浏览影视频道页10秒
taskInfo = await ViewFilmChannel(taskInfo);
// taskInfo = await ViewFilmChannel(taskInfo);

//浏览会员购页面10秒
taskInfo = ViewVipMall(taskInfo);
@@ -76,10 +135,12 @@ public override async Task DoTaskAsync(CancellationToken cancellationToken)

//领取购买任务
taskInfo = await BuyVipVideo(taskInfo);
taskInfo = await BuyVipProduct(taskInfo);
// taskInfo = await BuyVipProduct(taskInfo);
taskInfo = await BuyVipMall(taskInfo);

taskInfo.LogInfo(_logger);


}

[TaskInterceptor("测试Cookie")]
5 changes: 5 additions & 0 deletions src/Ray.BiliBiliTool.Config/Options/DailyTaskOptions.cs
Original file line number Diff line number Diff line change
@@ -18,6 +18,11 @@ public class DailyTaskOptions
/// </summary>
public bool IsShareVideo { get; set; }

/// <summary>
/// 是否开启专栏投币模式
/// </summary>
public bool IsDonateCoinForArticle { get; set; }

/// <summary>
/// 每日设定的投币数 [0,5]
/// </summary>
1 change: 1 addition & 0 deletions src/Ray.BiliBiliTool.Console/appsettings.json
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
"Cron": "0 15 * * *",
"IsWatchVideo": true, //是否观看视频
"IsShareVideo": true, //是否分享视频
"IsDonateCoinForArticle": false,
"NumberOfCoins": 5, //每日设定的投币数 [0,5]
"NumberOfProtectedCoins": 0, // 要保留的硬币数量 [0,int_max],0 为不保留,int_max 通常取 (2^31)-1
"SaveCoinsWhenLv6": false, //达到六级后是否开始白嫖[false,true]
395 changes: 395 additions & 0 deletions src/Ray.BiliBiliTool.DomainService/ArticleDomainService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.Article;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces;
using Ray.BiliBiliTool.Config.Options;
using Ray.BiliBiliTool.DomainService.Interfaces;

namespace Ray.BiliBiliTool.DomainService;

public class ArticleDomainService : IArticleDomainService
{
private readonly IArticleApi _articleApi;
private readonly BiliCookie _biliCookie;
private readonly ILogger<ArticleDomainService> _logger;
private readonly DailyTaskOptions _dailyTaskOptions;
private readonly ICoinDomainService _coinDomainService;
private readonly IAccountApi _accountApi;
private readonly IWbiDomainService _wbiDomainService;


/// <summary>
/// up的专栏总数缓存
/// </summary>
private readonly Dictionary<long, int> _upArticleCountDicCatch = new();

/// <summary>
/// 已对投币数量缓存
/// </summary>
private readonly Dictionary<string, int> _alreadyDonatedCoinCountCatch = new();

public ArticleDomainService(
IArticleApi articleApi,
BiliCookie biliCookie,
ILogger<ArticleDomainService> logger,
IOptionsMonitor<DailyTaskOptions> dailyTaskOptions,
ICoinDomainService coinDomainService,
IAccountApi accountApi, IWbiDomainService wbiDomainService)
{
_articleApi = articleApi;
_biliCookie = biliCookie;
_logger = logger;
_coinDomainService = coinDomainService;
_accountApi = accountApi;
_wbiDomainService = wbiDomainService;
_dailyTaskOptions = dailyTaskOptions.CurrentValue;
}



public async Task LikeArticle(long cvid)
{
await _articleApi.Like(cvid, _biliCookie.BiliJct);
}

/// <summary>
/// 投币专栏任务
/// </summary>
/// <returns></returns>
public async Task<bool> AddCoinForArticles()
{

var donateCoinsCounts = await CalculateDonateCoinsCounts();

if (donateCoinsCounts == 0)
{
// 没有可投的币相当于投币任务全部完成
return true;
}


int success = 0;
int tryCount = 10;

for (int i = 0; i <= tryCount && success < donateCoinsCounts; i++)
{
_logger.LogDebug("开始尝试第{num}次", i);

var upId = GetUpFromConfigUps();
var cvid = await GetRandomArticleFromUp(upId);
if (upId == 0 || cvid == 0)
{
_logger.LogInformation("未添加支持的Up主,任务跳过");
return false;
}

if (await AddCoinForArticle(cvid, upId))
{
// 点赞
if (_dailyTaskOptions.SelectLike)
{
await LikeArticle(cvid);
_logger.LogInformation("文章点赞成功");
}
success++;
}


}

if (success == donateCoinsCounts)
_logger.LogInformation("专栏投币任务完成");
else
{
_logger.LogInformation("投币尝试超过10次,已终止");
return false;
}


_logger.LogInformation("【硬币余额】{coin}", (await _accountApi.GetCoinBalance()).Data.Money ?? 0);

return true;
}


/// <summary>
/// 给某一篇专栏投币
/// </summary>
/// <param name="cvid">文章cvid</param>
/// <param name="mid">文章作者mid</param>
/// <returns>投币是否成功(false 投币失败,true 投币成功)</returns>
public async Task<bool> AddCoinForArticle(long cvid, long mid)
{
BiliApiResponse result;
try
{
var refer = $"https://www.bilibili.com/read/cv{cvid}/?from=search&spm_id_from=333.337.0.0";
result = await _articleApi.AddCoinForArticle(new AddCoinForArticleRequest(cvid, mid, _biliCookie.BiliJct),
refer);
}
catch (Exception)
{
return false;
}

if (result.Code == 0)
{
_logger.LogInformation("投币成功,经验+10 √");
return true;
}
else
{
_logger.LogError("投币错误 {message}", result.Message);
return false;
}
}


#region private

/// <summary>
/// 从某个up主中随机挑选一个专栏
/// </summary>
/// <param name="mid"></param>
/// <returns>专栏的cvid</returns>
private async Task<long> GetRandomArticleFromUp(long mid)
{
if (!_upArticleCountDicCatch.TryGetValue(mid, out int articleCount))
{
articleCount = await GetArticleCountOfUp(mid);
_upArticleCountDicCatch.Add(mid, articleCount);
}

// 专栏数为0时
if (articleCount == 0)
{
return 0;
}

var req = new SearchArticlesByUpIdDto()
{
Mid = mid,
Ps = 1,
Pn = new Random().Next(1, articleCount + 1)
};
var w_ridDto = await _wbiDomainService.GetWridAsync(req);

var fullDto = new SearchArticlesByUpIdFullDto()
{
Mid = mid,
Ps = req.Ps,
Pn = req.Pn,
w_rid = w_ridDto.w_rid,
wts = w_ridDto.wts
};

BiliApiResponse<SearchUpArticlesResponse> re = await _articleApi.SearchUpArticlesByUpId(fullDto);

if (re.Code != 0)
{
throw new Exception(re.Message);
}

ArticleInfo articleInfo = re.Data.Articles.FirstOrDefault();

_logger.LogDebug("获取到的专栏{cvid}({title})", articleInfo.Id, articleInfo.Title);

// 检查是否可投
if (!await IsCanDonate(articleInfo.Id))
{
return 0;
}

return articleInfo.Id;
}


// TODO 转变为异步代码
/// <summary>
/// 从支持UP主列表中随机挑选一位
/// </summary>
/// <returns>被挑选up主的mid</returns>
private long GetUpFromConfigUps()
{
if (_dailyTaskOptions.SupportUpIdList == null || _dailyTaskOptions.SupportUpIdList.Count == 0)
{
return 0;
}

try
{
long randomUpId =
_dailyTaskOptions.SupportUpIdList[new Random().Next(0, _dailyTaskOptions.SupportUpIdList.Count)];

if (randomUpId is 0 or long.MinValue) return 0;

if (randomUpId.ToString() == _biliCookie.UserId)
{
_logger.LogDebug("不能为自己投币");
return 0;
}
_logger.LogDebug("挑选出的up主为{UpId}",randomUpId);
return randomUpId;
}
catch (Exception e)
{
_logger.LogWarning("异常:{msg}", e);
}

return 0;
}

/// <summary>
/// 获取Up主专栏总数
/// </summary>
/// <param name="mid">up主mid</param>
/// <returns>专栏总数</returns>
/// <exception cref="Exception"></exception>
private async Task<int> GetArticleCountOfUp(long mid)
{
var req = new SearchArticlesByUpIdDto()
{
Mid = mid
};

var w_ridDto = await _wbiDomainService.GetWridAsync(req);

var fullDto = new SearchArticlesByUpIdFullDto()
{
Mid = mid,
w_rid = w_ridDto.w_rid,
wts = w_ridDto.wts
};

BiliApiResponse<SearchUpArticlesResponse> re = await _articleApi.SearchUpArticlesByUpId(fullDto);

if (re.Code != 0)
{
throw new Exception(re.Message);
}

return re.Data.Count;
}

/// <summary>
/// 计算所需要投的硬币数量
/// </summary>
/// <returns>硬币数量</returns>
private async Task<int> CalculateDonateCoinsCounts()
{
int needCoins = await GetNeedDonateCoinCounts();

int protectedCoins = _dailyTaskOptions.NumberOfProtectedCoins;
if (needCoins <= 0) return 0;

//投币前硬币余额
decimal coinBalance = await _coinDomainService.GetCoinBalance();
_logger.LogInformation("【投币前余额】 : {coinBalance}", coinBalance);
_ = int.TryParse(decimal.Truncate(coinBalance - protectedCoins).ToString(), out int unprotectedCoins);

if (coinBalance <= 0)
{
_logger.LogInformation("因硬币余额不足,今日暂不执行投币任务");
return 0;
}

if (coinBalance <= protectedCoins)
{
_logger.LogInformation("因硬币余额达到或低于保留值,今日暂不执行投币任务");
return 0;
}

//余额小于目标投币数,按余额投
if (coinBalance < needCoins)
{
_ = int.TryParse(decimal.Truncate(coinBalance).ToString(), out needCoins);
_logger.LogInformation("因硬币余额不足,目标投币数调整为: {needCoins}", needCoins);
return needCoins;
}

//投币后余额小于等于保护值,按保护值允许投
if (coinBalance - needCoins <= protectedCoins)
{
//排除需投等于保护后可投数量相等时的情况
if (unprotectedCoins != needCoins)
{
needCoins = unprotectedCoins;
_logger.LogInformation("因硬币余额投币后将达到或低于保留值,目标投币数调整为: {needCoins}", needCoins);
return needCoins;
}
}

return needCoins;
}

private async Task<int> GetNeedDonateCoinCounts()
{
int configCoins = _dailyTaskOptions.NumberOfCoins;

if (configCoins <= 0)
{
_logger.LogInformation("已配置为跳过投币任务");
return configCoins;
}

//已投的硬币
int alreadyCoins = await _coinDomainService.GetDonatedCoins();

int targetCoins = configCoins;

_logger.LogInformation("【今日已投】{already}枚", alreadyCoins);
_logger.LogInformation("【目标欲投】{already}枚", targetCoins);

if (targetCoins > alreadyCoins)
{
int needCoins = targetCoins - alreadyCoins;
_logger.LogInformation("【还需再投】{need}枚", needCoins);
return needCoins;
}

_logger.LogInformation("已完成投币任务,不需要再投啦~");
return 0;
}


private async Task<bool> IsCanDonate(long cvid)
{
try
{
if (_alreadyDonatedCoinCountCatch.Any(x => x.Key == cvid.ToString()))
{
_logger.LogDebug("重复专栏,丢弃处理");
return false;
}

if (!_alreadyDonatedCoinCountCatch.TryGetValue(cvid.ToString(), out int multiply))
{
multiply = (await _articleApi.SearchArticleInfo(cvid)).Data.Coin;
_alreadyDonatedCoinCountCatch.TryAdd(cvid.ToString(), multiply);
}

// 在网页端我测试时只能投一枚硬币,暂时设置最多投一枚
if (multiply >= 1)
{
return false;
}

return true;
}
catch (Exception e)
{
_logger.LogWarning("异常:{mag}", e);
return false;
}
}

#endregion
}
4 changes: 2 additions & 2 deletions src/Ray.BiliBiliTool.DomainService/DonateCoinDomainService.cs
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ public async Task AddCoinsForVideos()
}

if (success == needCoins)
_logger.LogInformation("投币任务完成");
_logger.LogInformation("视频投币任务完成");
else
_logger.LogInformation("投币尝试超过10次,已终止");

@@ -419,7 +419,7 @@ private async Task<bool> IsCanDonate(string aid)
//已经投满2个币的,不能再投
if (!await IsDonatedLessThenLimitCoinsForVideo(aid))
{
_logger.LogDebug("超出单个视频投币数量限制,丢弃处理", aid);
_logger.LogDebug("超出单个视频投币数量限制,丢弃处理");
return false;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading.Tasks;

namespace Ray.BiliBiliTool.DomainService.Interfaces;

public interface IArticleDomainService : IDomainService
{
Task<bool> AddCoinForArticle(long cvid, long mid);

Task<bool> AddCoinForArticles();

Task LikeArticle(long cvid);
}
8 changes: 8 additions & 0 deletions src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs
Original file line number Diff line number Diff line change
@@ -614,6 +614,14 @@ private async Task<List<FansMedalInfoDto>> GetFansMedalInfoList()
continue;
}

// 用以排除有牌子无直播间的up主
if (spaceInfo.Data.Live_room is null)
{
_logger.LogInformation("【主播】{name} 直播间id获取失败,已跳过",medal.Target_name);
continue;
}


var roomId = spaceInfo.Data.Live_room.Roomid;

// 获取直播间详细信息
21 changes: 21 additions & 0 deletions test/AppServiceTest/VipServiceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
using Ray.BiliBiliTool.Application.Contracts;
using Ray.BiliBiliTool.Infrastructure;

namespace AppServiceTest;

public class VipServiceTest
{
public VipServiceTest()
{
Program.CreateHost(new[] { "--ENVIRONMENT=Development" });
}

[Fact]
public async Task VipExpressTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();
var appService = scope.ServiceProvider.GetRequiredService<IVipBigPointAppService>();
await appService.VipExpress();
}
}
59 changes: 59 additions & 0 deletions test/BiliAgentTest/VipApiTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Threading.Tasks;
using Xunit;
using Microsoft.Extensions.DependencyInjection;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Dtos.VipTask;
using Ray.BiliBiliTool.Console;
using Ray.BiliBiliTool.Infrastructure;
using Ray.BiliBiliTool.Agent.BiliBiliAgent.Interfaces;
using Xunit.Abstractions;
namespace BiliAgentTest;

public class VipApiTest
{
private readonly ITestOutputHelper _output;
public VipApiTest(ITestOutputHelper output)
{
_output = output;
Program.CreateHost(new[] { "--ENVIRONMENT=Development" });
}

[Fact]
public async Task VipInfoTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();

var ck = scope.ServiceProvider.GetRequiredService<BiliCookie>();
var api = scope.ServiceProvider.GetRequiredService<IVipBigPointApi>();

var re = await api.GetVouchersInfo();
if (re.Code == 0)
{
var info = re.Data.List.Find(x => x.Type == 9);
if (info != null)
{
_output.WriteLine(info.State.ToString());
}
else
{
_output.WriteLine("error");
}
}
}


[Fact]
public async Task GetVipExperienceTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();

var ck = scope.ServiceProvider.GetRequiredService<BiliCookie>();
var api = scope.ServiceProvider.GetRequiredService<IVipBigPointApi>();
var re = await api.GetVipExperience(new VipExperienceRequest()
{
csrf = ck.BiliJct
});

_output.WriteLine(re.Message);
}
}
47 changes: 47 additions & 0 deletions test/DomainServiceTest/ArticleDomainServiceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Xunit.Abstractions;

namespace DomainServiceTest;

public class ArticleDomainServiceTest
{
private readonly ITestOutputHelper _output;
public ArticleDomainServiceTest(ITestOutputHelper output)
{
_output = output;
Program.CreateHost(new[] { "--ENVIRONMENT=Development" });
}

[Fact]
public async Task LikeArticleTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();
var config = Global.ConfigurationRoot;
var domainService = scope.ServiceProvider.GetRequiredService<IArticleDomainService>();
await domainService.LikeArticle(5806746);
}


[Fact]
public async Task AddCoinForArticleTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();
var config = Global.ConfigurationRoot;
var domainService = scope.ServiceProvider.GetRequiredService<IArticleDomainService>();

// 测试用的专栏:https://www.bilibili.com/read/cv5806746/?from=search&spm_id_from=333.337.0.0

await domainService.AddCoinForArticle(5806746, 486980924);
}


[Fact]
public async Task AddCoinForArticlesTest()
{
using var scope = Global.ServiceProviderRoot.CreateScope();
var config = Global.ConfigurationRoot;
var domainService = scope.ServiceProvider.GetRequiredService<IArticleDomainService>();
await domainService.AddCoinForArticles();
}


}

0 comments on commit aa73789

Please sign in to comment.