public async Task ProcessAsync(Sisters.WudiLib.Posts.Message message, HttpApiClient api) { // TODO 验证用户名是否合法 var(success, userInfo) = await OsuApi.GetUserInfoAsync(_userName, OsuMixedApi.Mode.Standard); if (!success) { await api.SendMessageAsync(message.Endpoint, "网络错误。"); return; } if (userInfo == null) { await api.SendMessageAsync(message.Endpoint, "没有此用户。"); return; } var dbResult = await Database.AddNewBindAsync(message.UserId, userInfo.Id, userInfo.Name, "自己绑定", message.UserId, userInfo.Name); if (dbResult.Success) { await api.SendMessageAsync(message.Endpoint, $"成功绑定为{userInfo.Name}。"); } else if (dbResult.Exception is DbUpdateException && dbResult.Exception.InnerException?.Message.Contains("Duplicate", StringComparison.Ordinal) == true) { await api.SendMessageAsync(message.Endpoint, "在已绑定的情况下不允许修改,如需修改请联系 bleatingsheep。"); } else { await api.SendMessageAsync(message.Endpoint, "数据库访问错误。"); FLogger.LogException(dbResult.Exception); } }
protected async Task <UserInfo> EnsureGetUserInfo(string name, Bleatingsheep.Osu.Mode mode) { var(success, result) = await OsuApi.GetUserInfoAsync(name, mode); ExecutingException.Ensure(success, "网络错误。"); ExecutingException.Ensure(result != null, "无此用户!"); return(result); }
protected async Task <(bool, UserInfo)> GetCachedUserInfo(int id, Bleatingsheep.Osu.Mode mode) { var hasCache = s_cache.TryGetValue <UserInfo>((id, mode), out var cachedInfo); if (hasCache) { return(true, cachedInfo); } var(success, userInfo) = await OsuApi.GetUserInfoAsync(id, mode); if (success) { s_cache.Set((id, mode), userInfo, CacheAvailable); return(true, userInfo); } else { // fail return(default);
private async Task ParseInfoAsync( HttpApiClient api, Endpoint sendBackEndpoint, GroupRequest r, int?osuId = null, TrustedUserInfo userInfo = default) { long userId = r.UserId; string comment = r.Comment; var hints = new List <Message>(); if (!string.IsNullOrEmpty(comment)) { var userNames = OsuHelper.DiscoverUsernames(comment).Where(n => !string.Equals(n, "osu", StringComparison.OrdinalIgnoreCase)); if (osuId != null) { bool success = userInfo != null; // 由于当前未从调用方法处获得上级 API 是否调用成功,在信息不为 null 时默认成功。 if (userInfo == null) { // 可能的重试。 (success, userInfo) = await OsuApi.GetUserInfoAsync(osuId.Value, Mode.Standard).ConfigureAwait(false); } _ = await ProcessApplicantReportAsync(hints, null, (success, userInfo)).ConfigureAwait(false); if (userInfo != null && !userNames.Any(n => string.Equals(userInfo.Name, n, StringComparison.OrdinalIgnoreCase))) {// 绑定不一致 hints.Add(new Message("警告:其绑定的账号与申请不符。")); } } else {// 忽略已绑定的情况,因为可能绑定不一致或者查询失败。 foreach (var name in userNames) { var userTuple = await OsuApi.GetUserInfoAsync(name, Bleatingsheep.Osu.Mode.Standard); //// 我想用 8.0 新语法 //hints.Add(new Message($"{info?.Name ?? name}: " + // $"{(success ? info == null ? "不存在此用户。" : $"PP: {info.Performance}, PC: {info.PlayCount}, TTH: {info.TotalHits}" : "查询失败。")}")); var info = await ProcessApplicantReportAsync(hints, name, userTuple).ConfigureAwait(false); if (info == null) {// 属于没有查到的情况(因为网络问题或者用户不存在),并且之前已经给出错误信息。 continue; } // 自动绑定,在请求消息完全匹配 osu! 用户名的前提下。 if (userNames.Count() == 1 && comment.TrimEnd().EndsWith($"答案:{name}", StringComparison.Ordinal) && info != null) { var bindingResult = await Database.AddNewBindAsync( qq : r.UserId, osuId : info.Id, osuName : info.Name, source : "Auto (Request)", operatorId : r.UserId, operatorName : info.Name).ConfigureAwait(false); if (bindingResult.Success) { hints.Add(new Message($"自动绑定为 {info.Name}")); goto binding_end; } else { hints.Add(new Message($"自动绑定失败。")); } } // 提供绑定并放行的捷径。 if (info?.Performance < 2500) { var ms = new MemoryStream(); using (var bw = new BinaryWriter(ms, Encoding.UTF8, true)) { bw.Write(userId); bw.Write(info.Id); bw.Write(r.Flag); } #pragma warning disable CA5351 // 不要使用损坏的加密算法 using var md5 = MD5.Create(); #pragma warning restore CA5351 // 不要使用损坏的加密算法 ms.Write(md5.ComputeHash(ms.ToArray())); var bytes = ms.ToArray(); var base64 = Convert.ToBase64String(bytes); await api.SendMessageAsync(sendBackEndpoint, $"(占位)绑定为 {info.Name} 并放行:#{base64}#"); } binding_end :; } } } if (hints.Count > 0) { var newLine = new Message("\r\n"); await api.SendMessageAsync(sendBackEndpoint, hints.Aggregate((m1, m2) => { return((m1.Sections.LastOrDefault()?.Type, m2.Sections.FirstOrDefault()?.Type) switch { ("text", "text") => m1 + newLine + m2, _ => m1 + m2, }); })).ConfigureAwait(false);
public async Task ProcessAsync(MessageContext context, HttpApiClient api) { var uid = await EnsureGetBindingIdAsync(context.UserId).ConfigureAwait(false); var apiTask = OsuApi.GetUserInfoAsync(uid, Bleatingsheep.Osu.Mode.Standard).ConfigureAwait(false); using (var page = await Chrome.OpenNewPageAsync().ConfigureAwait(false)) { await page.SetViewportAsync(new ViewPortOptions { DeviceScaleFactor = 2.75, Width = 1440, Height = 900, }).ConfigureAwait(false); await page.GoToAsync($"https://osu.ppy.sh/users/{uid}/osu").ConfigureAwait(false); // wait for load complete const string waitSelector = @"body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.osu-layout__section.osu-layout__section--users-extra > div > div > div > div:nth-child(2)"; await page.WaitForSelectorAsync(waitSelector).ConfigureAwait(false); const string bestSelector = "body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.osu-layout__section.osu-layout__section--users-extra > div > div > div > div:nth-child(2) > div > div.play-detail-list"; //ElementHandle bpsElement = await page.QuerySelectorAsync(bestSelector).ConfigureAwait(false); const string buttonSelector = "body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.osu-layout__section.osu-layout__section--users-extra > div > div > div > div:nth-child(2) > div > div.profile-extra-entries__item > button"; ElementHandle button = await page.QuerySelectorAsync(buttonSelector).ConfigureAwait(false); ElementHandle[]? bpList = default; int maxTryTimes = 4; while (button != null && maxTryTimes-- != 0) { //check bp counts. bpList = await page.QuerySelectorAllAsync(bestSelector + " > div").ConfigureAwait(false); await button.ClickAsync().ConfigureAwait(false); // wait for click complete await page.WaitForSelectorAsync(bestSelector + $" > div:nth-child({bpList.Length})", new WaitForSelectorOptions { Timeout = 8000 /*ms*/ }).ConfigureAwait(false); // seems that bp div adding and button availability changing is NOT synchronized // wait for button availability change await page.WaitForTimeoutAsync(500).ConfigureAwait(false); // requery button button = await page.QuerySelectorAsync(buttonSelector).ConfigureAwait(false); } if (bpList == null) { await api.SendMessageAsync(context.Endpoint, "查询失败。").ConfigureAwait(false); return; } // filter /* * bpList = document.querySelectorAll("body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.osu-layout__section.osu-layout__section--users-extra > div > div > div > div:nth-child(2) > div > div.play-detail-list > div"); * * bpList.forEach((element) => { var dateTime = element.querySelector("div.play-detail__group.play-detail__group--top > div.play-detail__detail > div > span.play-detail__time > time").getAttribute("datetime"); if (new Date() - Date.parse(dateTime) > 86400000) element.remove(); }) */ await page.EvaluateExpressionAsync(@"bpList = document.querySelectorAll(""body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.osu-layout__section.osu-layout__section--users-extra > div > div > div > div:nth-child(2) > div > div.play-detail-list > div"");").ConfigureAwait(false); await page.EvaluateExpressionAsync(@"bpList.forEach((element) => { var dateTime = element.querySelector(""div.play-detail__group.play-detail__group--top > div.play-detail__detail > div > span.play-detail__time > time"").getAttribute(""datetime""); if (new Date() - Date.parse(dateTime) > 86400000) element.remove(); })").ConfigureAwait(false); // check bpList = await page.QuerySelectorAllAsync(bestSelector + " > div").ConfigureAwait(false); if (bpList.Length == 0) { await api.SendMessageAsync(context.Endpoint, "最近 24 小时没有更新 bp。").ConfigureAwait(false); return; } //screenshot //delete pinned elements await(await page.QuerySelectorAsync("body > div.js-pinned-header.hidden-xs.no-print.nav2-header > div.nav2-header__body").ConfigureAwait(false)).EvaluateFunctionAsync(@"(element) => element.remove()").ConfigureAwait(false); await(await page.QuerySelectorAsync("body > div.osu-layout__section.osu-layout__section--full.js-content.community_profile > div > div > div > div.hidden-xs.page-extra-tabs.page-extra-tabs--profile-page.js-switchable-mode-page--scrollspy-offset").ConfigureAwait(false)).EvaluateFunctionAsync(@"(element) => element.remove()").ConfigureAwait(false); // add extra information await page.EvaluateExpressionAsync($@"document.querySelector(""{bestSelector}"").parentElement.parentElement.querySelector(""h3"").textContent += "" (用户名:"" + {JsonConvert.SerializeObject((await apiTask).Item2?.Name ?? "获取失败")} + "" 查询时间:"" + {JsonConvert.SerializeObject(DateTime.Now.ToString("yyyy-MM-dd H:mm)"))}").ConfigureAwait(false); //ElementHandle printElement = await page.QuerySelectorAsync(bestSelector).ConfigureAwait(false); var printElement = await(await page.QuerySelectorAsync(bestSelector).ConfigureAwait(false)) .EvaluateFunctionHandleAsync("(element) => element.parentNode.parentNode").ConfigureAwait(false) as ElementHandle; printElement ??= await page.QuerySelectorAsync(bestSelector).ConfigureAwait(false); var data = await printElement.ScreenshotDataAsync(new ScreenshotOptions()).ConfigureAwait(false); await api.SendMessageAsync(context.Endpoint, Message.ByteArrayImage(data)).ConfigureAwait(false); } }
public async Task ProcessAsync(MessageContext context, HttpApiClient api) { //await api.SendMessageAsync(context.Endpoint, $"[DEBUG] 比较:{_other};模式:{_mode}"); var id = await EnsureGetBindingIdAsync(context.UserId).ConfigureAwait(false); //var browser = GetBrowser(); byte[] data = null; var mode = Bleatingsheep.Osu.Mode.Standard; try { if (!string.IsNullOrEmpty(_mode)) { mode = Bleatingsheep.Osu.ModeExtensions.Parse(_mode); } } catch { await api.SendMessageAsync(context.Endpoint, "模式识别失败,fallback 到 Standard。").ConfigureAwait(false); } var url = $"http://hydrantweb/pptth/mini/{id}?height=350&mode={(int)mode}"; if (!string.IsNullOrEmpty(_other)) { var(_, user) = await OsuApi.GetUserInfoAsync(_other, mode); if (user == null) { ExecutingException.Ensure(false, "对比玩家错误"); } url += $"&compared={user.Id}"; } using (var page = await Chrome.OpenNewPageAsync().ConfigureAwait(false)) { await page.SetViewportAsync(new ViewPortOptions { DeviceScaleFactor = 1.15, Width = 640, Height = 350, }).ConfigureAwait(false); await page.GoToAsync(url).ConfigureAwait(false); await Task.Delay(0).ConfigureAwait(false); data = await page.ScreenshotDataAsync(new ScreenshotOptions { FullPage = true, //Type = ScreenshotType.Jpeg, //Quality = 100, }).ConfigureAwait(false); } var stopwatch = Stopwatch.StartNew(); var sendResponse = await api.SendMessageAsync(context.Endpoint, Message.ByteArrayImage(data)).ConfigureAwait(false); var elapsedTime = stopwatch.ElapsedMilliseconds; var failed = sendResponse is null; if (failed) { await api.SendMessageAsync(context.Endpoint, "图片发送失败(确定)").ConfigureAwait(false); } (failed ? FailedElapsed : SuccessfulElapsed).Add(elapsedTime); }