diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d118ecca..cc29e47c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.1.3 +- Code refactory ## 2.1.2 - Feature: enhancement CICD scripts - Fix[#728]: compatible with qinglong history versions diff --git a/common.props b/common.props index 34810691c..e3a3f38b0 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ Ray - 2.1.2 + 2.1.3 $(NoWarn);CS1591;CS0436 diff --git a/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs b/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs index 704e93d89..93e438fff 100644 --- a/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs +++ b/src/Ray.BiliBiliTool.DomainService/LiveDomainService.cs @@ -15,683 +15,684 @@ using Ray.BiliBiliTool.Config.Options; using Ray.BiliBiliTool.DomainService.Dtos; using Ray.BiliBiliTool.DomainService.Interfaces; -using Ray.BiliBiliTool.Infrastructure.Cookie; -namespace Ray.BiliBiliTool.DomainService +namespace Ray.BiliBiliTool.DomainService; + +/// +/// 直播 +/// +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 LiveFansMedalTaskOptions _liveFansMedalTaskOptions; + private readonly DailyTaskOptions _dailyTaskOptions; + private readonly SecurityOptions _securityOptions; + private readonly BiliCookie _biliCookie; + private readonly IWbiService _wbiService; + + public LiveDomainService(ILogger logger, + ILiveApi liveApi, + IRelationApi relationApi, + ILiveTraceApi liveTraceApi, + IUserInfoApi userInfoApi, + IOptionsMonitor dailyTaskOptions, + IOptionsMonitor liveLotteryTaskOptions, + IOptionsMonitor liveFansMedalTaskOptions, + IOptionsMonitor securityOptions, + IWbiService wbiService, + BiliCookie biliCookie) + { + _logger = logger; + _liveApi = liveApi; + _relationApi = relationApi; + _liveTraceApi = liveTraceApi; + _userInfoApi = userInfoApi; + _liveLotteryTaskOptions = liveLotteryTaskOptions.CurrentValue; + _dailyTaskOptions = dailyTaskOptions.CurrentValue; + _liveFansMedalTaskOptions = liveFansMedalTaskOptions.CurrentValue; + _securityOptions = securityOptions.CurrentValue; + _wbiService = wbiService; + _biliCookie = biliCookie; + } + /// - /// 直播 + /// 本次通过天选关注的主播 /// - 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 LiveFansMedalTaskOptions _liveFansMedalTaskOptions; - private readonly DailyTaskOptions _dailyTaskOptions; - private readonly SecurityOptions _securityOptions; - private readonly BiliCookie _biliCookie; - private readonly IWbiService _wbiService; - - public LiveDomainService(ILogger logger, - ILiveApi liveApi, - IRelationApi relationApi, - ILiveTraceApi liveTraceApi, - IUserInfoApi userInfoApi, - IOptionsMonitor dailyTaskOptions, - IOptionsMonitor liveLotteryTaskOptions, - IOptionsMonitor liveFansMedalTaskOptions, - IOptionsMonitor securityOptions, - IWbiService wbiService, - BiliCookie biliCookie) - { - _logger = logger; - _liveApi = liveApi; - _relationApi = relationApi; - _liveTraceApi = liveTraceApi; - _userInfoApi = userInfoApi; - _liveLotteryTaskOptions = liveLotteryTaskOptions.CurrentValue; - _dailyTaskOptions = dailyTaskOptions.CurrentValue; - _liveFansMedalTaskOptions = liveFansMedalTaskOptions.CurrentValue; - _securityOptions = securityOptions.CurrentValue; - _wbiService = wbiService; - _biliCookie = biliCookie; - } + private List _tianXuanFollowed = new(); - /// - /// 本次通过天选关注的主播 - /// - private List _tianXuanFollowed = new(); + /// + /// 开始抽奖前最后一个关注的up + /// + private long _lastFollowUpId; - /// - /// 开始抽奖前最后一个关注的up - /// - private long _lastFollowUpId; + /// + /// 直播签到 + /// + public async Task LiveSign() + { + var response = await _liveApi.Sign(); - /// - /// 直播签到 - /// - public async Task LiveSign() + if (response.Code == 0) { - var response = await _liveApi.Sign(); - - if (response.Code == 0) - { - _logger.LogInformation("【签到结果】成功"); - _logger.LogInformation("【本次获取】{text},{special}", response.Data.Text, response.Data.SpecialText); - } - else - { - _logger.LogInformation("【签到结果】失败"); - _logger.LogInformation("【原因】{msg}", response.Message); - } + _logger.LogInformation("【签到结果】成功"); + _logger.LogInformation("【本次获取】{text},{special}", response.Data.Text, response.Data.SpecialText); } - - /// - /// 直播中心银瓜子兑换B币 - /// - /// 兑换银瓜子后硬币余额 - public async Task ExchangeSilver2Coin() + else { - var result = false; + _logger.LogInformation("【签到结果】失败"); + _logger.LogInformation("【原因】{msg}", response.Message); + } + } - if (_dailyTaskOptions.DayOfExchangeSilver2Coin == 0) - { - _logger.LogInformation("已配置为关闭,跳过"); - return false; - } + /// + /// 直播中心银瓜子兑换B币 + /// + /// 兑换银瓜子后硬币余额 + public async Task ExchangeSilver2Coin() + { + var result = false; - int targetDay = _dailyTaskOptions.DayOfExchangeSilver2Coin == -2 - ? DateTime.Today.Day - : _dailyTaskOptions.DayOfExchangeSilver2Coin == -1 - ? DateTime.Today.LastDayOfMonth().Day - : _dailyTaskOptions.DayOfExchangeSilver2Coin; + if (_dailyTaskOptions.DayOfExchangeSilver2Coin == 0) + { + _logger.LogInformation("已配置为关闭,跳过"); + return false; + } - _logger.LogInformation("【目标兑换日期】{targetDay}号", targetDay); - _logger.LogInformation("【今天】{day}号", DateTime.Today.Day); + int targetDay = _dailyTaskOptions.DayOfExchangeSilver2Coin == -2 + ? DateTime.Today.Day + : _dailyTaskOptions.DayOfExchangeSilver2Coin == -1 + ? DateTime.Today.LastDayOfMonth().Day + : _dailyTaskOptions.DayOfExchangeSilver2Coin; - if (DateTime.Today.Day != targetDay) - { - _logger.LogInformation("跳过"); - return false; - } + _logger.LogInformation("【目标兑换日期】{targetDay}号", targetDay); + _logger.LogInformation("【今天】{day}号", DateTime.Today.Day); - BiliApiResponse queryStatus = await _liveApi.GetLiveWalletStatus(); - _logger.LogInformation("【银瓜子余额】 {silver}", queryStatus.Data.Silver); - _logger.LogInformation("【硬币余额】 {coin}", queryStatus.Data.Coin); - _logger.LogInformation("【今日剩余兑换次数】 {left}", queryStatus.Data.Silver_2_coin_left); + if (DateTime.Today.Day != targetDay) + { + _logger.LogInformation("跳过"); + return false; + } - if (queryStatus.Data.Silver_2_coin_left <= 0) return false; + BiliApiResponse queryStatus = await _liveApi.GetLiveWalletStatus(); + _logger.LogInformation("【银瓜子余额】 {silver}", queryStatus.Data.Silver); + _logger.LogInformation("【硬币余额】 {coin}", queryStatus.Data.Coin); + _logger.LogInformation("【今日剩余兑换次数】 {left}", queryStatus.Data.Silver_2_coin_left); - _logger.LogInformation("开始尝试兑换..."); - Silver2CoinRequest request = new(_biliCookie.BiliJct); - var response = await _liveApi.Silver2Coin(request); - if (response.Code == 0) - { - result = true; - _logger.LogInformation("【兑换结果】成功兑换 {coin} 枚硬币", response.Data.Coin); - _logger.LogInformation("【银瓜子余额】 {silver}", response.Data.Silver); - } - else - { - _logger.LogInformation("【兑换结果】失败"); - _logger.LogInformation("【原因】{reason}", response.Message); - } + if (queryStatus.Data.Silver_2_coin_left <= 0) return false; - return result; + _logger.LogInformation("开始尝试兑换..."); + Silver2CoinRequest request = new(_biliCookie.BiliJct); + var response = await _liveApi.Silver2Coin(request); + if (response.Code == 0) + { + result = true; + _logger.LogInformation("【兑换结果】成功兑换 {coin} 枚硬币", response.Data.Coin); + _logger.LogInformation("【银瓜子余额】 {silver}", response.Data.Silver); } - - #region 天选时刻抽奖 - - /// - /// 天选抽奖 - /// - public async Task TianXuan() + else { - _tianXuanFollowed = new List(); - - if (_liveLotteryTaskOptions.AutoGroupFollowings) - { - //获取此时最后一个关注的up,此后再新增的关注,与参与成功的抽奖,取交集,就是本地新增的天选关注 - _lastFollowUpId = await GetLastFollowUpId(); - } - - //获取直播的分区 - List areaList = (await _liveApi.GetAreaList()).Data.Data; + _logger.LogInformation("【兑换结果】失败"); + _logger.LogInformation("【原因】{reason}", response.Message); + } - //遍历分区 - int count = 0; - foreach (var area in areaList) - { - _logger.LogInformation("【扫描分区】{area}...{newLine}", area.Name, Environment.NewLine); + return result; + } - string defaultSort = ""; - //每个分区下搜索5页 - for (int i = 1; i < 6; i++) - { - var reData = (await _liveApi.GetList(area.Id, i, sortType: defaultSort)).Data; - foreach (var item in reData.List ?? new List()) - { - if (item.Pendant_info == null || item.Pendant_info.Count == 0) continue; - var suc = item.Pendant_info.TryGetValue("2", out var pendant); - if (!suc) continue; - if (pendant.Pendent_id != 504) continue; - count++; - - await TryJoinTianXuan(item); - } - - if (reData.Has_more != 1) break; - defaultSort = reData.New_tags.FirstOrDefault()?.Sort_type ?? ""; - } + #region 天选时刻抽奖 - defaultSort = ""; - } + /// + /// 天选抽奖 + /// + public async Task TianXuan() + { + _tianXuanFollowed = new List(); - if (count == 0) - { - _logger.LogInformation("未搜索到直播间"); - return; - } + if (_liveLotteryTaskOptions.AutoGroupFollowings) + { + //获取此时最后一个关注的up,此后再新增的关注,与参与成功的抽奖,取交集,就是本地新增的天选关注 + _lastFollowUpId = await GetLastFollowUpId(); } - public async Task TryJoinTianXuan(ListItemDto target) - { - _logger.LogDebug("【房间】{name}", target.Title); - try - { - //黑名单 - if (_liveLotteryTaskOptions.DenyUidList.Contains(target.Uid.ToString())) - { - _logger.LogDebug("黑名单,跳过"); - return; - } + //获取直播的分区 + List areaList = (await _liveApi.GetAreaList()).Data.Data; - CheckTianXuanDto check = (await _liveApi.CheckTianXuan(target.Roomid)).Data; + //遍历分区 + int count = 0; + foreach (var area in areaList) + { + _logger.LogInformation("【扫描分区】{area}...{newLine}", area.Name, Environment.NewLine); - if (check == null) + string defaultSort = ""; + //每个分区下搜索5页 + for (int i = 1; i < 6; i++) + { + var reData = (await _liveApi.GetList(area.Id, i, sortType: defaultSort)).Data; + foreach (var item in reData.List ?? new List()) { - _logger.LogDebug("数据异常,跳过"); - return; - } + if (item.Pendant_info == null || item.Pendant_info.Count == 0) continue; + var suc = item.Pendant_info.TryGetValue("2", out var pendant); + if (!suc) continue; + if (pendant.Pendent_id != 504) continue; + count++; - if (check.Status != TianXuanStatus.Enable) - { - _logger.LogDebug("已开奖,跳过{newLine}", Environment.NewLine); - return; + await TryJoinTianXuan(item); } - //根据配置过滤 - if (!check.AwardNameIsSatisfied(_liveLotteryTaskOptions.IncludeAwardNameList, - _liveLotteryTaskOptions.ExcludeAwardNameList)) - { - _logger.LogDebug("不满足配置的筛选条件,跳过{newLine}", Environment.NewLine); - return; - } + if (reData.Has_more != 1) break; + defaultSort = reData.New_tags.FirstOrDefault()?.Sort_type ?? ""; + } - //是否需要赠礼 - if (check.Gift_price > 0) - { - _logger.LogDebug("【赠礼】{gift}", check.GiftDesc); - _logger.LogDebug("需赠送礼物,跳过{newLine}", Environment.NewLine); - return; - } + defaultSort = ""; + } - //条件 - if (check.Require_type != RequireType.None && check.Require_type != RequireType.Follow) - { - _logger.LogDebug("【条件】{text}", check.Require_text); - _logger.LogDebug("要求粉丝勋章,跳过"); - return; - } + if (count == 0) + { + _logger.LogInformation("未搜索到直播间"); + return; + } + } - _logger.LogInformation("【房间】{name}", target.ShortTitle); - _logger.LogInformation("【主播】{name}({id})", target.Uname, target.Uid); - _logger.LogInformation("【奖品】{name}【条件】{text}", check.Award_name, check.Require_text); + public async Task TryJoinTianXuan(ListItemDto target) + { + _logger.LogDebug("【房间】{name}", target.Title); + try + { + //黑名单 + if (_liveLotteryTaskOptions.DenyUidList.Contains(target.Uid.ToString())) + { + _logger.LogDebug("黑名单,跳过"); + return; + } - var request = new JoinTianXuanRequest - { - Id = check.Id, Gift_id = check.Gift_id, Gift_num = check.Gift_num, Csrf = _biliCookie.BiliJct - }; - var re = await _liveApi.Join(request); - if (re.Code == 0) - { - _logger.LogInformation("【抽奖】成功 √{newLine}", Environment.NewLine); - if (check.Require_type == RequireType.Follow) - _tianXuanFollowed.AddIfNotExist(target, x => x.Uid == target.Uid); - return; - } + CheckTianXuanDto check = (await _liveApi.CheckTianXuan(target.Roomid)).Data; - _logger.LogInformation("【抽奖】失败"); - _logger.LogInformation("【原因】{msg}{newLine}", re.Message, Environment.NewLine); - } - catch (Exception ex) + if (check == null) { - _logger.LogWarning("【异常】{msg},{detail}{newLine}", ex.Message, ex, Environment.NewLine); - //ignore + _logger.LogDebug("数据异常,跳过"); + return; } - } - /// - /// 将本次抽奖新增的关注统一转移到指定分组中 - /// - public async Task GroupFollowing() - { - if (!_tianXuanFollowed.Any()) + if (check.Status != TianXuanStatus.Enable) { - _logger.LogInformation("未关注主播"); + _logger.LogDebug("已开奖,跳过{newLine}", Environment.NewLine); return; } - _logger.LogInformation("【抽奖的主播】{ups}", - string.Join(",", _tianXuanFollowed.Select(x => x.Uname))); - - //目标分组up集合 - List targetUps = await GetNeedGroup(); - _logger.LogInformation("【将自动分组】{ups}", - string.Join(",", targetUps.Select(x => x.Uname))); + //根据配置过滤 + if (!check.AwardNameIsSatisfied(_liveLotteryTaskOptions.IncludeAwardNameList, + _liveLotteryTaskOptions.ExcludeAwardNameList)) + { + _logger.LogDebug("不满足配置的筛选条件,跳过{newLine}", Environment.NewLine); + return; + } - if (!targetUps.Any()) + //是否需要赠礼 + if (check.Gift_price > 0) { + _logger.LogDebug("【赠礼】{gift}", check.GiftDesc); + _logger.LogDebug("需赠送礼物,跳过{newLine}", Environment.NewLine); return; } - //目标分组Id - long targetGroupId = await GetOrCreateTianXuanGroupId(); + //条件 + if (check.Require_type != RequireType.None && check.Require_type != RequireType.Follow) + { + _logger.LogDebug("【条件】{text}", check.Require_text); + _logger.LogDebug("要求粉丝勋章,跳过"); + return; + } - //执行批量分组 - var referer = string.Format(RelationApiConstant.CopyReferer, _biliCookie.UserId); - var req = new CopyUserToGroupRequest( - targetUps.Select(x => x.Uid).ToList(), - targetGroupId.ToString(), - _biliCookie.BiliJct); - var re = await _relationApi.CopyUpsToGroup(req, referer); + _logger.LogInformation("【房间】{name}", target.ShortTitle); + _logger.LogInformation("【主播】{name}({id})", target.Uname, target.Uid); + _logger.LogInformation("【奖品】{name}【条件】{text}", check.Award_name, check.Require_text); - if (re.Code == 0) + var request = new JoinTianXuanRequest { - _logger.LogInformation("【分组结果】全部成功"); - } - else + Id = check.Id, + Gift_id = check.Gift_id, + Gift_num = check.Gift_num, + Csrf = _biliCookie.BiliJct + }; + var re = await _liveApi.Join(request); + if (re.Code == 0) { - _logger.LogWarning("【分组结果】失败"); - _logger.LogWarning("【原因】{msg}", re.Message); + _logger.LogInformation("【抽奖】成功 √{newLine}", Environment.NewLine); + if (check.Require_type == RequireType.Follow) + _tianXuanFollowed.AddIfNotExist(target, x => x.Uid == target.Uid); + return; } - } - - /// - /// 获取抽奖前最后一个关注的up - /// - /// - private async Task GetLastFollowUpId() + _logger.LogInformation("【抽奖】失败"); + _logger.LogInformation("【原因】{msg}{newLine}", re.Message, Environment.NewLine); + } + catch (Exception ex) { - var followings = await _relationApi - .GetFollowings(new GetFollowingsRequest(long.Parse(_biliCookie.UserId), FollowingsOrderType.TimeDesc)); - return followings.Data.List.FirstOrDefault()?.Mid ?? 0; + _logger.LogWarning("【异常】{msg},{detail}{newLine}", ex.Message, ex, Environment.NewLine); + //ignore } + } - /// - /// 获取本次需要自动分组的主播 - /// - /// - private async Task> GetNeedGroup() + /// + /// 将本次抽奖新增的关注统一转移到指定分组中 + /// + public async Task GroupFollowing() + { + if (!_tianXuanFollowed.Any()) { - List addUpIds = new(); + _logger.LogInformation("未关注主播"); + return; + } - //获取最后一个upId之后关注的所有upId - var followings = await _relationApi - .GetFollowings(new GetFollowingsRequest(long.Parse(_biliCookie.UserId), FollowingsOrderType.TimeDesc)); + _logger.LogInformation("【抽奖的主播】{ups}", + string.Join(",", _tianXuanFollowed.Select(x => x.Uname))); - foreach (UpInfo item in followings.Data.List) - { - if (item.Mid == _lastFollowUpId) - { - break; - } + //目标分组up集合 + List targetUps = await GetNeedGroup(); + _logger.LogInformation("【将自动分组】{ups}", + string.Join(",", targetUps.Select(x => x.Uname))); - addUpIds.Add(item.Mid); - } + if (!targetUps.Any()) + { + return; + } - //和成功抽奖的主播取交集 - List target = new(); - foreach (var listItemDto in _tianXuanFollowed) - { - if (addUpIds.Contains(listItemDto.Uid)) - target.Add(listItemDto); - } + //目标分组Id + long targetGroupId = await GetOrCreateTianXuanGroupId(); - return target; - } + //执行批量分组 + var referer = string.Format(RelationApiConstant.CopyReferer, _biliCookie.UserId); + var req = new CopyUserToGroupRequest( + targetUps.Select(x => x.Uid).ToList(), + targetGroupId.ToString(), + _biliCookie.BiliJct); + var re = await _relationApi.CopyUpsToGroup(req, referer); - /// - /// 获取或创建天选时刻分组 - /// - /// - private async Task GetOrCreateTianXuanGroupId() + if (re.Code == 0) { - //获取天选分组Id,没有就创建 - long groupId = 0; - string referer = string.Format(RelationApiConstant.GetTagsReferer, _biliCookie.UserId); - var groups = await _relationApi.GetTags(referer); - var tianXuanGroup = groups.Data.FirstOrDefault(x => x.Name == "天选时刻"); - if (tianXuanGroup == null) - { - _logger.LogInformation("“天选时刻”分组不存在,尝试创建..."); - //创建一个 - var createRe = - await _relationApi.CreateTag(new CreateTagRequest { Tag = "天选时刻", Csrf = _biliCookie.BiliJct }); - groupId = createRe.Data.Tagid; - _logger.LogInformation("创建成功"); - } - else - { - _logger.LogInformation("“天选时刻”分组已存在"); - groupId = tianXuanGroup.Tagid; - } - - return groupId; + _logger.LogInformation("【分组结果】全部成功"); + } + else + { + _logger.LogWarning("【分组结果】失败"); + _logger.LogWarning("【原因】{msg}", re.Message); } + } - #endregion - public async Task SendDanmakuToFansMedalLive() - { - if (!await CheckLiveCookie()) return; + /// + /// 获取抽奖前最后一个关注的up + /// + /// + private async Task GetLastFollowUpId() + { + var followings = await _relationApi + .GetFollowings(new GetFollowingsRequest(long.Parse(_biliCookie.UserId), FollowingsOrderType.TimeDesc)); + return followings.Data.List.FirstOrDefault()?.Mid ?? 0; + } + + /// + /// 获取本次需要自动分组的主播 + /// + /// + private async Task> GetNeedGroup() + { + List addUpIds = new(); - var infoList = await GetFansMedalInfoList(); + //获取最后一个upId之后关注的所有upId + var followings = await _relationApi + .GetFollowings(new GetFollowingsRequest(long.Parse(_biliCookie.UserId), FollowingsOrderType.TimeDesc)); - foreach (var info in infoList) + foreach (UpInfo item in followings.Data.List) + { + if (item.Mid == _lastFollowUpId) { - var medal = info.MedalInfo; + break; + } - _logger.LogInformation("【直播间】{liveRoomName}", medal.Target_name); - _logger.LogInformation("【粉丝牌】{medalName}", medal.Medal_info.Medal_name); - _logger.LogInformation("正在发送弹幕..."); + addUpIds.Add(item.Mid); + } - // 通过空间主页信息获取直播间 id - var liveHostUserId = medal.Medal_info.Target_id; - var req = new GetSpaceInfoDto() { mid = liveHostUserId }; - await _wbiService.SetWridAsync(req); + //和成功抽奖的主播取交集 + List target = new(); + foreach (var listItemDto in _tianXuanFollowed) + { + if (addUpIds.Contains(listItemDto.Uid)) + target.Add(listItemDto); + } - var spaceInfo = await _userInfoApi.GetSpaceInfo(req); - if (spaceInfo.Code != 0) - { - _logger.LogError("【获取直播间信息】失败"); - _logger.LogError("【原因】{message}", spaceInfo.Message); - return; - } + return target; + } - var successCount = 0; - var failedCount = 0; + /// + /// 获取或创建天选时刻分组 + /// + /// + private async Task GetOrCreateTianXuanGroupId() + { + //获取天选分组Id,没有就创建 + long groupId = 0; + string referer = string.Format(RelationApiConstant.GetTagsReferer, _biliCookie.UserId); + var groups = await _relationApi.GetTags(referer); + var tianXuanGroup = groups.Data.FirstOrDefault(x => x.Name == "天选时刻"); + if (tianXuanGroup == null) + { + _logger.LogInformation("“天选时刻”分组不存在,尝试创建..."); + //创建一个 + var createRe = + await _relationApi.CreateTag(new CreateTagRequest { Tag = "天选时刻", Csrf = _biliCookie.BiliJct }); + groupId = createRe.Data.Tagid; + _logger.LogInformation("创建成功"); + } + else + { + _logger.LogInformation("“天选时刻”分组已存在"); + groupId = tianXuanGroup.Tagid; + } - // 发送弹幕 + return groupId; + } - while (successCount < _liveFansMedalTaskOptions.SendDanmakuNumber && - failedCount < _liveFansMedalTaskOptions.SendDanmakugiveUpThreshold) - { - var sendResult = await _liveApi.SendLiveDanmuku(new SendLiveDanmukuRequest( - _biliCookie.BiliJct, - spaceInfo.Data.Live_room.Roomid, - _liveFansMedalTaskOptions.DanmakuContent)); - - if (sendResult.Code != 0) - { - _logger.LogError("【弹幕发送】失败"); - _logger.LogError("【原因】{message}", sendResult.Message); - failedCount++; - } - else - successCount++; - - var delay = new Random().Next(2000, 4000); - await Task.Delay(delay); - } + #endregion + public async Task SendDanmakuToFansMedalLive() + { + if (!await CheckLiveCookie()) return; - _logger.LogInformation("【弹幕发送】发送情况:你向主播 {name} 发送弹幕{success}/{total}", spaceInfo.Data.Name, - successCount, successCount + failedCount); - } - } + var infoList = await GetFansMedalInfoList(); - public async Task SendHeartBeatToFansMedalLive() + foreach (var info in infoList) { - if (!await CheckLiveCookie()) return; + var medal = info.MedalInfo; - var infoList = new List(); - (await GetFansMedalInfoList()).FindAll(info => info.LiveRoomInfo.Live_Status != 0).ForEach(medal => - infoList.Add(new(medal.RoomId, medal.LiveRoomInfo, new(), 0, 0)) - ); + _logger.LogInformation("【直播间】{liveRoomName}", medal.Target_name); + _logger.LogInformation("【粉丝牌】{medalName}", medal.Medal_info.Medal_name); + _logger.LogInformation("正在发送弹幕..."); - if (infoList.Count == 0) + // 通过空间主页信息获取直播间 id + var liveHostUserId = medal.Medal_info.Target_id; + var req = new GetSpaceInfoDto() { mid = liveHostUserId }; + await _wbiService.SetWridAsync(req); + + var spaceInfo = await _userInfoApi.GetSpaceInfo(req); + if (spaceInfo.Code != 0) { - _logger.LogInformation("【直播观看时长】跳过,未检测到符合条件的主播"); + _logger.LogError("【获取直播间信息】失败"); + _logger.LogError("【原因】{message}", spaceInfo.Message); return; } - var Now = () => new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds(); + var successCount = 0; + var failedCount = 0; + + // 发送弹幕 - while (infoList.Min( - info => info.FailedTimes >= _liveFansMedalTaskOptions.HeartBeatSendGiveUpThreshold - ? int.MaxValue - : info.HeartBeatCount) - < _liveFansMedalTaskOptions.HeartBeatNumber) + while (successCount < _liveFansMedalTaskOptions.SendDanmakuNumber && + failedCount < _liveFansMedalTaskOptions.SendDanmakugiveUpThreshold) { - foreach (var info in infoList) + var sendResult = await _liveApi.SendLiveDanmuku(new SendLiveDanmukuRequest( + _biliCookie.BiliJct, + spaceInfo.Data.Live_room.Roomid, + _liveFansMedalTaskOptions.DanmakuContent)); + + if (sendResult.Code != 0) { - // 忽略连续失败超过上限的直播间 - 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 = await _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}\"]") - ); - } - else - { - heartBeatResult = await _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}\"]") - ); - } - - 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); + _logger.LogError("【弹幕发送】失败"); + _logger.LogError("【原因】{message}", sendResult.Message); + failedCount++; } + else + successCount++; + + var delay = new Random().Next(2000, 4000); + await Task.Delay(delay); } - var successCount = infoList.Count(info => info.HeartBeatCount >= _liveFansMedalTaskOptions.HeartBeatNumber); - _logger.LogInformation("【直播观看时长】完成情况:{success}/{total} ", successCount, infoList.Count); + + _logger.LogInformation("【弹幕发送】发送情况:你向主播 {name} 发送弹幕{success}/{total}", spaceInfo.Data.Name, + successCount, successCount + failedCount); } + } - /// - /// 点赞直播间 - /// - public async Task LikeFansMedalLive() + public async Task SendHeartBeatToFansMedalLive() + { + if (!await CheckLiveCookie()) return; + + var infoList = new List(); + (await GetFansMedalInfoList()).FindAll(info => info.LiveRoomInfo.Live_Status != 0).ForEach(medal => + infoList.Add(new(medal.RoomId, medal.LiveRoomInfo, new(), 0, 0)) + ); + + if (infoList.Count == 0) { - if (!await CheckLiveCookie()) return; + _logger.LogInformation("【直播观看时长】跳过,未检测到符合条件的主播"); + return; + } - var infoList = await GetFansMedalInfoList(); - infoList = infoList.FindAll(info => info.LiveRoomInfo.Live_Status != 0); - _logger.LogInformation("当前开播直播间数量:{num}", infoList.Count); + 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) { - // Clike_Time 暂时设置为等于设置的LikeNumber,不清楚是否会被风控,我自己抓包最大值为10 - var request = new LikeLiveRoomRequest(info.RoomId, _biliCookie.BiliJct, - _liveFansMedalTaskOptions.LikeNumber, - info.LiveRoomInfo.Uid, _biliCookie.UserId); + // 忽略连续失败超过上限的直播间 + if (info.FailedTimes >= _liveFansMedalTaskOptions.HeartBeatSendGiveUpThreshold) continue; - var result = await _liveApi.LikeLiveRoom(request.RawTextBuild()); - if (result.Code == 0) + string uuid = Guid.NewGuid().ToString(); + var current = Now(); + if (current - info.LastBeatTime <= (LiveFansMedalTaskOptions.HeartBeatInterval + 5) * 1000) { - _logger.LogInformation("【点赞直播间】{roomId} 完成", info.RoomId); + int sleepTime = (int)((LiveFansMedalTaskOptions.HeartBeatInterval + 5) * 1000 - + (current - info.LastBeatTime)); + _logger.LogDebug("【休眠】{time} 毫秒", sleepTime); + Thread.Sleep(sleepTime); } - else + + // Heart Beat 接口 + var timestamp = Now(); + BiliApiResponse heartBeatResult = null; + if (info.HeartBeatCount == 0) { - _logger.LogError("【点赞直播间】{roomId} 时候出现错误", info.RoomId); - _logger.LogError("【原因】{message}", result.Message); + heartBeatResult = await _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}\"]") + ); } - - var delay = new Random().Next(5000, 8000); - await Task.Delay(delay); - } - } - - private async Task> GetFansMedalInfoList() - { - _logger.LogInformation("【获取直播列表】获取拥有粉丝牌的直播列表"); - var medalWallInfo = await _liveApi.GetMedalWall(_biliCookie.UserId); - - 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) + else { - _logger.LogInformation("粉丝牌等级为 {level},观看将不再增长亲密度,跳过", medal.Medal_info.Level); - continue; + heartBeatResult = await _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}\"]") + ); } - // 通过空间主页信息获取直播间 id - var liveHostUserId = medal.Medal_info.Target_id; - var req = new GetSpaceInfoDto() { mid = liveHostUserId }; - await _wbiService.SetWridAsync(req); + info.LastBeatTime = Now(); - var spaceInfo = await _userInfoApi.GetSpaceInfo(req); - if (spaceInfo.Code != 0) + if (heartBeatResult != null && heartBeatResult.Data != null) { - _logger.LogError("【获取空间信息】失败"); - _logger.LogError("【原因】{message}", spaceInfo.Message); - continue; + info.HeartBeatInfo.Secret_key = heartBeatResult.Data.Secret_key; + info.HeartBeatInfo.Secret_rule = heartBeatResult.Data.Secret_rule; + info.HeartBeatInfo.Timestamp = heartBeatResult.Data.Timestamp; } - // 用以排除有牌子无直播间的up主 - if (spaceInfo.Data.Live_room is null) + if (heartBeatResult == null || heartBeatResult.Code != 0) { - _logger.LogInformation("【主播】{name} 直播间id获取失败,已跳过", medal.Target_name); + _logger.LogError("【心跳包】直播间 {room} 发送失败", info.RoomId); + _logger.LogError("【原因】{message}", heartBeatResult != null ? heartBeatResult.Message : ""); + info.FailedTimes += 1; continue; } + info.HeartBeatCount += 1; + info.FailedTimes = 0; - var roomId = spaceInfo.Data.Live_room.Roomid; + _logger.LogInformation("【直播间】{roomId} 的第 {index} 个心跳包发送成功", info.RoomId, info.HeartBeatCount); + } + } - // 获取直播间详细信息 - var liveRoomInfo = await _liveApi.GetLiveRoomInfo(roomId); - if (liveRoomInfo.Code != 0) - { - _logger.LogError("【获取直播间信息】失败"); - _logger.LogError("【原因】{message}", liveRoomInfo.Message); - continue; - } + var successCount = infoList.Count(info => info.HeartBeatCount >= _liveFansMedalTaskOptions.HeartBeatNumber); + _logger.LogInformation("【直播观看时长】完成情况:{success}/{total} ", successCount, infoList.Count); + } + + /// + /// 点赞直播间 + /// + public async Task LikeFansMedalLive() + { + if (!await CheckLiveCookie()) return; + + var infoList = await GetFansMedalInfoList(); + infoList = infoList.FindAll(info => info.LiveRoomInfo.Live_Status != 0); + _logger.LogInformation("当前开播直播间数量:{num}", infoList.Count); + foreach (var info in infoList) + { + // Clike_Time 暂时设置为等于设置的LikeNumber,不清楚是否会被风控,我自己抓包最大值为10 + var request = new LikeLiveRoomRequest(info.RoomId, _biliCookie.BiliJct, + _liveFansMedalTaskOptions.LikeNumber, + info.LiveRoomInfo.Uid, _biliCookie.UserId); - infoList.Add(new FansMedalInfoDto(roomId, medal, liveRoomInfo.Data)); + var result = await _liveApi.LikeLiveRoom(request.RawTextBuild()); + if (result.Code == 0) + { + _logger.LogInformation("【点赞直播间】{roomId} 完成", info.RoomId); } + else + { + _logger.LogError("【点赞直播间】{roomId} 时候出现错误", info.RoomId); + _logger.LogError("【原因】{message}", result.Message); + } + + var delay = new Random().Next(5000, 8000); + await Task.Delay(delay); + } + } + + private async Task> GetFansMedalInfoList() + { + _logger.LogInformation("【获取直播列表】获取拥有粉丝牌的直播列表"); + var medalWallInfo = await _liveApi.GetMedalWall(_biliCookie.UserId); - return infoList; + if (medalWallInfo.Code != 0) + { + _logger.LogError("【获取直播列表】失败"); + _logger.LogError("【原因】{message}", medalWallInfo.Message); + return new List(); } - /// - /// 自动配置直播相关 Cookie,来兼容较低版本中保存的 Cookie 配置 - /// - /// - /// bool 成功配置 or not - /// - private async Task CheckLiveCookie() + var infoList = new List(); + foreach (var medal in medalWallInfo.Data.List) { - // 检测 _biliCookie 是否正确配置 - if (!string.IsNullOrWhiteSpace(_biliCookie.LiveBuvid)) return true; + _logger.LogInformation("【主播】{name} ", medal.Target_name); + if (_liveFansMedalTaskOptions.IsSkipLevel20Medal && medal.Medal_info.Level >= 20) + { + _logger.LogInformation("粉丝牌等级为 {level},观看将不再增长亲密度,跳过", medal.Medal_info.Level); + continue; + } - try + // 通过空间主页信息获取直播间 id + var liveHostUserId = medal.Medal_info.Target_id; + var req = new GetSpaceInfoDto() { mid = liveHostUserId }; + await _wbiService.SetWridAsync(req); + + var spaceInfo = await _userInfoApi.GetSpaceInfo(req); + if (spaceInfo.Code != 0) { - _logger.LogInformation("检测到直播 Cookie 未正确配置,尝试自动配置中..."); + _logger.LogError("【获取空间信息】失败"); + _logger.LogError("【原因】{message}", spaceInfo.Message); + continue; + } - // 请求主播主页来正确配置 cookie - var liveHome = await _liveApi.GetLiveHome(); - var liveHomeContent = - JsonConvert.DeserializeObject(await liveHome.Content.ReadAsStringAsync()); - if (liveHomeContent.Code != 0) - { - throw new Exception(liveHomeContent.Message); - } + // 用以排除有牌子无直播间的up主 + if (spaceInfo.Data.Live_room is null) + { + _logger.LogInformation("【主播】{name} 直播间id获取失败,已跳过", medal.Target_name); + continue; + } - List liveCookies = liveHome.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value - .ToList(); - _biliCookie.MergeCurrentCookie(liveCookies); - _logger.LogDebug("LiveBuvid {value}", _biliCookie.LiveBuvid); - _logger.LogInformation("直播 Cookie 配置成功!"); + var roomId = spaceInfo.Data.Live_room.Roomid; + + // 获取直播间详细信息 + var liveRoomInfo = await _liveApi.GetLiveRoomInfo(roomId); + if (liveRoomInfo.Code != 0) + { + _logger.LogError("【获取直播间信息】失败"); + _logger.LogError("【原因】{message}", liveRoomInfo.Message); + continue; } - catch (Exception exception) + + infoList.Add(new FansMedalInfoDto(roomId, medal, liveRoomInfo.Data)); + } + + return infoList; + } + + /// + /// 自动配置直播相关 Cookie,来兼容较低版本中保存的 Cookie 配置 + /// + /// + /// bool 成功配置 or not + /// + private async Task CheckLiveCookie() + { + // 检测 _biliCookie 是否正确配置 + if (!string.IsNullOrWhiteSpace(_biliCookie.LiveBuvid)) return true; + + try + { + _logger.LogInformation("检测到直播 Cookie 未正确配置,尝试自动配置中..."); + + // 请求主播主页来正确配置 cookie + var liveHome = await _liveApi.GetLiveHome(); + var liveHomeContent = + JsonConvert.DeserializeObject(await liveHome.Content.ReadAsStringAsync()); + if (liveHomeContent.Code != 0) { - _logger.LogError("【配置直播Cookie】失败,放弃执行后续任务..."); - _logger.LogError("【原因】{message}", exception.Message); - return false; + throw new Exception(liveHomeContent.Message); } - return true; + List liveCookies = liveHome.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value + .ToList(); + _biliCookie.MergeCurrentCookie(liveCookies); + + _logger.LogDebug("LiveBuvid {value}", _biliCookie.LiveBuvid); + _logger.LogInformation("直播 Cookie 配置成功!"); } + catch (Exception exception) + { + _logger.LogError("【配置直播Cookie】失败,放弃执行后续任务..."); + _logger.LogError("【原因】{message}", exception.Message); + return false; + } + + return true; } } \ No newline at end of file