public async Task <IActionResult> AuthPassword() { OAuthPasswordAuth auth; using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) { auth = JsonConvert.DeserializeObject <OAuthPasswordAuth>(await reader.ReadToEndAsync()); } var oauthClient = await DboAuthClient.GetClient(_ctx, auth.ClientId); if (oauthClient == null) { return(BadRequest(new { code = 403, message = "Client Doesn't exists!" })); } if (oauthClient.Secret != auth.ClientSecret) { return(BadRequest(new { code = 404, message = "invalid secret!" })); } var user = await DbUser.GetDbUser(_ctx, auth.Username); if (user == null || !user.IsPassword(Hex.ToHex(Crypto.GetMd5(auth.Password)))) { return(BadRequest(new { code = 400, message = "Username or password is incorrect" })); } var key = Convert.FromBase64String(_cfg.Esc); return(Authorized(key, new[] { new Claim(ClaimTypes.Name, user.Id.ToString()) })); }
public static async Task <(bool Pass, DbScore score)> ParseScore(SoraDbContext ctx, string encScore, string iv, string osuVersion) { var decryptedScore = Crypto.DecryptString( Convert.FromBase64String(encScore), Encoding.ASCII.GetBytes(string.Format(PRIVATE_KEY, osuVersion)), Convert.FromBase64String(iv) ); var x = decryptedScore.Split(':'); var score = new DbScore { FileMd5 = x[0], ScoreOwner = await DbUser.GetDbUser(ctx, x[1]), Count300 = int.Parse(x[3]), Count100 = int.Parse(x[4]), Count50 = int.Parse(x[5]), CountGeki = int.Parse(x[6]), CountKatu = int.Parse(x[7]), CountMiss = int.Parse(x[8]), TotalScore = int.Parse(x[9]), MaxCombo = short.Parse(x[10]), Mods = (Mod)uint.Parse(x[13]), PlayMode = (PlayMode)byte.Parse(x[15]), Date = DateTime.Now, }; score.UserId = score.ScoreOwner?.Id ?? 0; return(bool.Parse(x[14]), score); }
public async Task <IActionResult> GetSearchDirect( [FromQuery(Name = "m")] int playMode, [FromQuery(Name = "r")] int rankedStatus, [FromQuery(Name = "p")] int page, [FromQuery(Name = "q")] string query, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass, [FromServices] SoraDbContext ctx, [FromServices] Pisstaube pisstaube ) { Response.ContentType = "text/plain"; var user = await DbUser.GetDbUser(ctx, userName); if (user == null) { return(Ok("err: pass")); } if (!user.IsPassword(pass)) { return(Ok("err: pass")); } var searchResult = await pisstaube.SearchDirectAsync(query, rankedStatus, playMode, page); return(Ok(searchResult)); // this no longer needs to be cached... }
public async Task <IActionResult> GetReplay( [FromQuery(Name = "c")] int replayId, [FromQuery(Name = "m")] PlayMode mode, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass, [FromServices] SoraDbContext ctx ) { var user = await DbUser.GetDbUser(ctx, userName); if (user == null) { return(Ok("err: pass")); } if (!user.IsPassword(pass)) { return(Ok("err: pass")); } var s = await DbScore.GetScore(ctx, replayId); if (s == null) { return(NotFound()); } return(File(System.IO.File.OpenRead("data/replays/" + s.ReplayMd5), "binary/octet-stream", s.ReplayMd5)); }
public async Task <IActionResult> GetDirectNp( [FromQuery(Name = "s")] int setId, [FromQuery(Name = "b")] int beatmapId, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass, [FromServices] SoraDbContext ctx, [FromServices] Pisstaube pisstaube ) { Response.ContentType = "text/plain"; var user = await DbUser.GetDbUser(ctx, userName); if (user == null) { return(Ok("err: pass")); } if (!user.IsPassword(pass)) { return(Ok("err: pass")); } return(Ok(await(setId != 0 ? pisstaube.FetchDirectBeatmapSetAsync(setId) : pisstaube.FetchDirectBeatmapAsync(beatmapId)))); }
public async Task <ActionResult> Get(string userName) { var user = await DbUser.GetDbUser(_context, userName); if (user == null) { return(NotFound(new { code = 410, message = "User not found!" })); } var lb = await DbLeaderboard.GetLeaderboardAsync(_context, user); return(Ok(new { user.Id, user.UserName, Permissions = Permission.From(user.Permissions).Perms, user.Status, user.StatusUntil, user.StatusReason, Achievements = DbAchievement.FromString(_context, user.Achievements ?? ""), Country = "XX", globalRank = lb.GetPosition(_context, PlayMode.Osu), Accuracy = lb.GetAccuracy(_context, PlayMode.Osu), Performance = lb.PerformancePointsOsu, TotalScore = lb.TotalScoreOsu, RankedScore = lb.RankedScoreOsu, PlayCount = lb.PlayCountOsu })); }
public async Task<IActionResult> GetSearchDirect( [FromQuery(Name = "m")] int playMode, [FromQuery(Name = "r")] int rankedStatus, [FromQuery(Name = "p")] int page, [FromQuery(Name = "q")] string query, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass ) { var user = await DbUser.GetDbUser(_ctx, userName); if (user == null) return Ok("err: pass"); if (!user.IsPassword(pass)) return Ok("err: pass"); var cacheHash = Hex.ToHex(Crypto.GetMd5($"m{playMode}r{rankedStatus}p{page}q{query}")); if (_cache.TryGet($"sora:DirectSearches:{cacheHash}", out string cachedData)) return Ok(cachedData); Response.ContentType = "text/plain"; var searchResult = await _pisstaube.SearchDirectAsync(query, rankedStatus, playMode, page); _cache.Set($"sora:DirectSearches:{cacheHash}", cachedData = searchResult, TimeSpan.FromMinutes(10)); return Ok(cachedData); }
public Sora(SoraDbContext ctx, IServiceProvider provider, PresenceService ps, ChannelService cs, EventManager ev ) { _provider = provider; _ctx = ctx; _ps = ps; _cs = cs; _ev = ev; #region DEFAULT COMMANDS RegisterCommandClass <RestrictCommand>(); RegisterCommandClass <DebugCommand>(); #endregion ctx.Migrate(); // this will fail if bot already exists! DbUser.RegisterUser(_ctx, Permission.From(Permission.GROUP_ADMIN), "Aisuru", "*****@*****.**", Crypto.RandomString(32), false, PasswordVersion.V2, 100); _dbUser = DbUser.GetDbUser(ctx, 100).Result; }
public async Task<IActionResult> GetReplay( [FromQuery(Name = "c")] int replayId, [FromQuery(Name = "m")] PlayMode mode, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass ) { var user = await DbUser.GetDbUser(_ctx, userName); if (user == null) return Ok("err: pass"); if (!user.IsPassword(pass)) return Ok("err: pass"); var s = await DbScore.GetScore(_ctx, replayId); if (s == null) return NotFound(); return File(System.IO.File.OpenRead("data/replays/" + s.ReplayMd5), "binary/octet-stream", s.ReplayMd5); }
public async Task <ActionResult> Get() { if (!User.Identity.IsAuthenticated) { return(Ok(new { authentication = "basic" })); } var user = await DbUser.GetDbUser(_context, int.Parse(User.Identity.Name)); if (user == null) { return(Ok(new { authentication = "basic" })); } var lb = await DbLeaderboard.GetLeaderboardAsync(_context, user); return(Ok(new { user.Id, user.UserName, user.Permissions, user.Status, user.StatusUntil, user.StatusReason, Achievements = DbAchievement.FromString(_context, user.Achievements ?? ""), Country = "XX", globalRank = lb.GetPosition(_context, PlayMode.Osu), Accuracy = lb.GetAccuracy(_context, PlayMode.Osu), Performance = lb.PerformancePointsOsu, TotalScore = lb.TotalScoreOsu, RankedScore = lb.RankedScoreOsu, PlayCount = lb.PlayCountOsu, Followers = await DbFriend.GetFollowerCount(_context, user.Id), })); }
public async Task<IActionResult> GetDirectNp( [FromQuery(Name = "s")] int setId, [FromQuery(Name = "b")] int beatmapId, [FromQuery(Name = "u")] string userName, [FromQuery(Name = "h")] string pass ) { var user = await DbUser.GetDbUser(_ctx, userName); if (user == null) return Ok("err: pass"); if (!user.IsPassword(pass)) return Ok("err: pass"); var cacheHash = Hex.ToHex(Crypto.GetMd5($"s{setId}|b{beatmapId}")); if (_cache.TryGet($"sora:DirectNP:{cacheHash}", out string cachedData)) return Ok(cachedData); if (!string.IsNullOrEmpty(cachedData)) return Ok(cachedData); Response.ContentType = "text/plain"; if (setId != 0) { _cache.Set($"sora:DirectNP:{cacheHash}", cachedData = await _pisstaube.FetchDirectBeatmapSetAsync(setId), TimeSpan.FromMinutes(10)); } else { _cache.Set($"sora:DirectNP:{cacheHash}", cachedData = await _pisstaube.FetchDirectBeatmapAsync(beatmapId), TimeSpan.FromMinutes(10)); } return Ok(cachedData); }
public async Task <ActionResult> Get([FromQuery] int offset = 0, [FromQuery] PlayMode mode = PlayMode.Osu) { var lb = (await _context.Leaderboard .Where(x => x.Owner.Status != UserStatusFlags.Suspended || x.Owner.Status != UserStatusFlags.Restricted) .OrderByDescending(x => x.PerformancePointsOsu) .Skip(offset) .Take(50) .ToListAsync()) .Select(l => new LBResponse { Id = l.OwnerId, Username = DbUser.GetDbUser(_context, l.OwnerId).Result.UserName, Accuracy = l.GetAccuracy(_context, mode), Performance = mode switch { PlayMode.Osu => l.PerformancePointsOsu, PlayMode.Taiko => l.PerformancePointsTaiko, PlayMode.Ctb => l.PerformancePointsCtb, PlayMode.Mania => l.PerformancePointsMania, _ => 0 },
public async Task <IActionResult> AuthWebsite() { PasswordAuth auth; using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) { auth = JsonConvert.DeserializeObject <PasswordAuth>(await reader.ReadToEndAsync()); } var user = await DbUser.GetDbUser(_ctx, auth.Username); if (user == null) { return(BadRequest(new { code = 401, message = "Username is incorrect!" })); } if (!user.IsPassword(Hex.ToHex(Crypto.GetMd5(auth.Password)))) { return(BadRequest(new { code = 402, message = "Password is incorrect" })); } var key = Convert.FromBase64String(_cfg.Esc); return(Authorized(key, new[] { new Claim(ClaimTypes.Name, user.Id.ToString()) })); }
public async Task Receive() { var stream = _client.GetStream(); var bytes = new byte[4096]; _writer = new StreamWriter(stream); // TODO: Finish http://www.networksorcery.com/enp/protocol/irc.htm try { int i; while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) { if (_exit) { return; } var data = Encoding.UTF8.GetString(bytes, 0, i).Split("\n"); foreach (var cmd in data.Select(d => d.Split(" ")) .Where(cmd => !string.IsNullOrEmpty(cmd[0])) .Where(cmd => !string.IsNullOrWhiteSpace(cmd[0]))) { switch (cmd[0]) { case "USER": if (_authorized) { SendCodeMessage(462, "Unauthorized command (already authorized!)"); continue; } if (cmd.Length < 2) { SendCodeMessage(451, "You have not registered"); continue; } _nickname = cmd[1].Trim(); var rUser = DbUser.GetDbUser(_ctx, cmd[1].Trim()).Result; if (rUser == null) { SendCodeMessage(451, "You have not registered"); continue; } _user = rUser; if (_authorized) { SendCodeMessage(462, "Unauthorized command (already authorized!)"); continue; } if (rUser?.IsPassword(Hex.ToHex(Crypto.GetMd5(_pass.Trim()))) != true) { if (string.IsNullOrEmpty(_nickname)) { _nickname = "anonymous"; } Logger.Info(_pass); SendCodeMessage(464, "Password incorrect"); continue; } _presence = new Presence(); _presence.User = _user; _presence["STATUS"] = new UserStatus(); _presence["IRC"] = true; WriteConnectionResponse(); WriteStatus(); WriteMotd(); _authorized = true; break; case "JOIN": if (_presence == null || !_authorized) { SendCodeMessage(444, "You're not logged in"); break; } if (_cs.TryGet(cmd[1].Trim(), out var channel)) { SendCodeMessage(403, $"{cmd[1]}: No such Channel!"); break; } channel.Join(_presence); /* * if (!) * { * SendCodeMessage(482, "You're not allowed to Join this Channel!"); * break; * } */ if (channel.Topic == "") { SendCodeMessage(331, "No topic is set", channel: channel.Name); } else { SendCodeMessage(332, channel.Topic, channel: channel.Name); } var uList = string.Join( " ", channel.Presences .Select( ps => { var ret = ps.User.UserName.Replace(" ", "_"); if (ps.User.Permissions.HasPermission(Permission.GROUP_CHAT_MOD)) { ret = "@" + ret; } else if (ps.User.Permissions.HasPermission( Permission.GROUP_DONATOR )) { ret = "+" + ret; } return(ret); }) .ToArray() ); uList += " @olSora"; SendCodeMessage(353, $"{uList}", channel: "= " + channel.Name); SendCodeMessage(366, "End of NAMES list", channel: channel.Name); break; case "PING": if (_presence == null || !_authorized) { SendCodeMessage(444, "You're not logged in"); break; } await _evmng.RunEvent(EventType.BanchoPong, new BanchoPongArgs { Pr = _presence }); SendMessage($"PONG {_cfg.Server.Hostname}: {cmd[1]}"); break; case "PASS": _pass = cmd[1]; break; case "QUIT": _client.Close(); _parent.RemoveTcpClient(this); return; // Ignored Commands case "NICK": case "CAP": break; default: Logger.Debug(string.Join(" ", cmd)); if (!_authorized) { SendCodeMessage(444, "You're not logged in"); } break; } } _writer.Flush(); } } catch (Exception e) { Logger.Err(e); _client.Close(); } }
public async Task OnLoginRequest(BanchoLoginRequestArgs args) { try { var sw = new Stopwatch(); sw.Start(); var loginData = LoginParser.ParseLogin(args.Reader); if (loginData == null) { Exception(args.Writer); return; } var cacheKey = $"sora:user:{loginData.GetHashCode()}"; if (!_cache.TryGet(cacheKey, out Presence presence)) { var dbUser = await DbUser.GetDbUser(_ctx, loginData.Username); if (dbUser == null) { LoginFailed(args.Writer); return; } if (!dbUser.IsPassword(loginData.Password)) { _logger.Log(LogLevel.Warning, $"{LCOL.RED}{dbUser.UserName} " + $"{LCOL.PURPLE}({dbUser.Id}) " + $"{LCOL.RED}Failed {LCOL.WHITE}to Login!"); LoginFailed(args.Writer); return; } if (args.IpAddress != "127.0.0.1" && args.IpAddress != "0.0.0.0") { try { var data = Localisation.GetData(args.IpAddress); args.Pr.Info.Longitude = data.Location.Longitude ?? 0; args.Pr.Info.Latitude = data.Location.Latitude ?? 0; args.Pr.Info.CountryId = Localisation.StringToCountryId(data.Country.IsoCode); } catch { // Ignored.. doesn't matter too much } } args.Pr.User = dbUser; args.Pr.Info.TimeZone = (byte)loginData.Timezone; var lb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbUser); args.Pr["LB"] = lb; args.Pr.Stats.TotalScore = lb.TotalScoreOsu; args.Pr.Stats.RankedScore = lb.RankedScoreOsu; args.Pr.Stats.PerformancePoints = (ushort)lb.PerformancePointsOsu; args.Pr.Stats.PlayCount = (uint)lb.PlayCountOsu; args.Pr.Stats.Accuracy = (float)lb.GetAccuracy(_ctx, PlayMode.Osu); args.Pr.Stats.Position = lb.GetPosition(_ctx, PlayMode.Osu); //args.pr["BLOCK_NON_FRIENDS_DM"] = loginData.BlockNonFriendDMs; _cache.Set(cacheKey, args.Pr, TimeSpan.FromMinutes(30)); } else { var t = args.Pr.Token; args.Pr = presence; args.Pr.Token = t; } if (_pcs.TryGet(args.Pr.User.Id, out var oldPresence)) { oldPresence.ActiveMatch?.Leave(args.Pr); oldPresence.Spectator?.Leave(args.Pr); _pcs.Leave(oldPresence); } _pcs.Join(args.Pr); Success(args.Writer, args.Pr.User.Id); args.Pr.Push(new ProtocolNegotiation()); args.Pr.Push(new UserPresence(args.Pr)); args.Pr.Push(new HandleUpdate(args.Pr)); args.Pr.Info.ClientPermission = LoginPermissions.User; if (args.Pr.User.Permissions == Permission.GROUP_DONATOR) { args.Pr.Info.ClientPermission |= LoginPermissions.Supporter; } if (args.Pr.User.Permissions == Permission.GROUP_ADMIN) { args.Pr.Info.ClientPermission |= LoginPermissions.BAT | LoginPermissions.Administrator | LoginPermissions.Moderator; } if (args.Pr.User.Permissions == Permission.GROUP_DEVELOPER) { args.Pr.Info.ClientPermission |= LoginPermissions.Developer; } if (!args.Pr.User.Permissions.HasPermission(Permission.GROUP_DONATOR)) { if (_cfg.Server.FreeDirect) { args.Pr.Push(new LoginPermission(LoginPermissions.User | LoginPermissions.Supporter | args.Pr.Info.ClientPermission)); } } else { args.Pr.Push(new LoginPermission(args.Pr.Info.ClientPermission)); } args.Pr.Push(new FriendsList(DbFriend.GetFriends(_ctx, args.Pr.User.Id).ToList())); args.Pr.Push(new PresenceBundle(_pcs.GetUserIds(args.Pr).ToList())); foreach (var chanAuto in _cs.ChannelsAutoJoin) { if ((chanAuto.Status & ChannelStatus.AdminOnly) != 0 && args.Pr.User.Permissions == Permission.ADMIN_CHANNEL) { args.Pr.Push(new ChannelAvailableAutojoin(chanAuto)); } else if ((chanAuto.Status & ChannelStatus.AdminOnly) == 0) { args.Pr.Push(new ChannelAvailableAutojoin(chanAuto)); } args.Pr.Push(new ChannelJoinSuccess(chanAuto)); chanAuto.Join(args.Pr); } foreach (var channel in _cs.Channels) { if ((channel.Status & ChannelStatus.AdminOnly) != 0 && args.Pr.User.Permissions == Permission.ADMIN_CHANNEL) { args.Pr.Push(new ChannelAvailable(channel)); } else if ((channel.Status & ChannelStatus.AdminOnly) == 0) { args.Pr.Push(new ChannelAvailable(channel)); } } _pcs.Push(new PresenceSingle(args.Pr.User.Id)); _pcs.Join(args.Pr); args.Pr.WritePackets(args.Writer.BaseStream); sw.Stop(); _logger.Log(LogLevel.Debug, "Login Time:\nMS: ", sw.Elapsed.TotalMilliseconds); _logger.Log(LogLevel.Information, $"{LCOL.RED}{args.Pr.User.UserName} {LCOL.PURPLE}( {args.Pr.User.Id} ) {LCOL.WHITE}has logged in!" ); args.Pr["LAST_PONG"] = DateTime.Now; } catch (Exception ex) { Logger.Err(ex); Exception(args.Writer); } }
public async Task<IActionResult> GetScoreResult( [FromQuery(Name = "v")] ScoreboardType type, [FromQuery(Name = "c")] string fileMd5, [FromQuery(Name = "f")] string f, [FromQuery(Name = "m")] PlayMode playMode, [FromQuery(Name = "i")] int i, [FromQuery(Name = "mods")] Mod mods, [FromQuery(Name = "us")] string us, [FromQuery(Name = "ha")] string pa, [FromServices] IServiceProvider serviceProvider) { try { var dbUser = await DbUser.GetDbUser(_ctx, us); var user = dbUser?.ToUser(); if (dbUser?.IsPassword(pa) != true) return Ok("error: pass"); var cacheHash = Hex.ToHex( Crypto.GetMd5( $"{fileMd5}{playMode}{mods}{type}{user.Id}{user.UserName}" ) ); if (_cache.TryGet($"sora:Scoreboards:{cacheHash}", out string cachedData)) return Ok(cachedData); var scores = await DbScore.GetScores(_ctx, fileMd5, dbUser, playMode, type == ScoreboardType.Friends, type == ScoreboardType.Country, type == ScoreboardType.Mods, mods); var beatmap = DbBeatmap.GetBeatmap(_ctx, fileMd5); BeatmapSet apiSet; if (beatmap == null) { apiSet = await _pisstaube.FetchBeatmapSetAsync(fileMd5); if (apiSet == null) goto JustContinue; var beatmaps = DbBeatmap.FromBeatmapSet(apiSet).ToList(); var beatmapChecksums = beatmaps.Select(s => s.FileMd5); var dbBeatmaps = _ctx.Beatmaps.Where(rset => beatmapChecksums.Any(lFileMd5 => rset.FileMd5 == lFileMd5)) .ToList(); var concurrentLock = new object(); var pool = serviceProvider.GetRequiredService<DbContextPool<SoraDbContext>>(); Task.WaitAll(beatmaps.Select(rawBeatmap => Task.Run(async () => { var context = pool.Rent(); var dbBeatmap = dbBeatmaps.FirstOrDefault(s => s.FileMd5 == rawBeatmap.FileMd5); if (dbBeatmap != null && (dbBeatmap.Flags & DbBeatmapFlags.RankedFreeze) != 0) { rawBeatmap.RankedStatus = dbBeatmap.RankedStatus; rawBeatmap.Flags = dbBeatmap.Flags; } context.Beatmaps.AddOrUpdate(rawBeatmap); await context.SaveChangesAsync(); pool.Return(context); })).ToArray()); beatmap = beatmaps.FirstOrDefault(s => s.FileMd5 == fileMd5); } JustContinue: var ownScore = await DbScore.GetLatestScore(_ctx, new DbScore { FileMd5 = fileMd5, UserId = user.Id, PlayMode = playMode, TotalScore = 0 }); var sScores = scores.Select(s => new Score { Count100 = s.Count100, Count50 = s.Count50, Count300 = s.Count300, Date = s.Date, Mods = s.Mods, CountGeki = s.CountGeki, CountKatu = s.CountKatu, CountMiss = s.CountMiss, FileMd5 = s.FileMd5, MaxCombo = s.MaxCombo, PlayMode = s.PlayMode, TotalScore = s.TotalScore, UserId = s.UserId, UserName = s.ScoreOwner.UserName, }).ToList(); // Fetch the correct position for sScore for (var j = 0; j < scores.Count; j++) { sScores[j].Position = await scores[j].Position(_ctx); } Score ownsScore = null; if (ownScore != null) ownsScore = new Score { Count100 = ownScore.Count100, Count50 = ownScore.Count50, Count300 = ownScore.Count300, Date = ownScore.Date, Mods = ownScore.Mods, CountGeki = ownScore.CountGeki, CountKatu = ownScore.CountKatu, CountMiss = ownScore.CountMiss, FileMd5 = ownScore.FileMd5, MaxCombo = ownScore.MaxCombo, PlayMode = ownScore.PlayMode, TotalScore = ownScore.TotalScore, UserId = dbUser.Id, UserName = dbUser.UserName, Position = await ownScore.Position(_ctx) }; BeatmapSet set = null; if (beatmap != null) set = new BeatmapSet { SetID = beatmap.Id, Artist = beatmap.Artist, Title = beatmap.Title, RankedStatus = beatmap.RankedStatus, ChildrenBeatmaps = new List<Beatmap> { new Beatmap { FileMD5 = beatmap.FileMd5, DiffName = beatmap.DiffName, ParentSetID = beatmap.SetId, BeatmapID = beatmap.Id, Mode = beatmap.PlayMode } } }; var sboard = new Scoreboard(set?.ChildrenBeatmaps.FirstOrDefault(bm => bm.FileMD5 == fileMd5), set, sScores, ownsScore); _cache.Set($"sora:Scoreboards:{cacheHash}", cachedData = sboard.ToOsuString(), TimeSpan.FromSeconds(30)); return Ok(cachedData); } catch (Exception ex) { Logger.Err(ex); return Ok("Failed"); } }
public async Task<IActionResult> PostSubmitModular() { if (!Directory.Exists("data/replays")) Directory.CreateDirectory("data/replays"); string encScore = Request.Form["score"]; string iv = Request.Form["iv"]; string osuver = Request.Form["osuver"]; string passwd = Request.Form["pass"]; var (pass, score) = ScoreSubmissionParser.ParseScore(encScore, iv, osuver); var dbUser = await DbUser.GetDbUser(_ctx, score.UserName); if (dbUser == null) return Ok("error: pass"); if (!dbUser.IsPassword(passwd)) return Ok("error: pass"); if (!_ps.TryGet(dbUser.Id, out var pr)) return Ok("error: pass"); // User not logged in in Bancho! if (!pass || !RankedMods.IsRanked(score.Mods)) { var lb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbUser); lb.IncreasePlaycount(score.PlayMode); lb.IncreaseScore((ulong) score.TotalScore, false, score.PlayMode); await lb.SaveChanges(_ctx); // Send to other People await _ev.RunEvent( EventType.BanchoUserStatsRequest, new BanchoUserStatsRequestArgs {UserIds = new List<int> {score.Id}, Pr = pr} ); // Send to self await _ev.RunEvent( EventType.BanchoSendUserStatus, new BanchoSendUserStatusArgs {Status = pr.Status, Pr = pr} ); return Ok("Thanks for your hard work! onii-chyan~"); // even though, we're Sora, we can still be cute! } var replayFileData = Request.Form.Files.GetFile("score"); var dbScore = new DbScore { Accuracy = score.ComputeAccuracy(), Count100 = score.Count100, Count50 = score.Count50, Count300 = score.Count300, Date = score.Date, Mods = score.Mods, CountGeki = score.CountGeki, CountKatu = score.CountKatu, CountMiss = score.CountMiss, FileMd5 = score.FileMd5, MaxCombo = score.MaxCombo, PlayMode = score.PlayMode, ScoreOwner = dbUser, TotalScore = score.TotalScore, UserId = dbUser.Id }; await _pisstaube.DownloadBeatmapAsync(dbScore.FileMd5); await using (var m = new MemoryStream()) { replayFileData.CopyTo(m); m.Position = 0; dbScore.ReplayMd5 = Hex.ToHex(Crypto.GetMd5(m)) ?? string.Empty; if (!string.IsNullOrEmpty(dbScore.ReplayMd5)) { await using var replayFile = System.IO.File.Create($"data/replays/{dbScore.ReplayMd5}"); m.Position = 0; m.WriteTo(replayFile); m.Close(); replayFile.Close(); } } dbScore.PerformancePoints = dbScore.ComputePerformancePoints(); var oldScore = await DbScore.GetLatestScore(_ctx, dbScore); var oldLb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbScore.ScoreOwner); var oldStdPos = oldLb.GetPosition(_ctx, dbScore.PlayMode); var oldAcc = oldLb.GetAccuracy(_ctx, dbScore.PlayMode); double newAcc; if (oldScore != null && oldScore.TotalScore <= dbScore.TotalScore) { _ctx.Remove(oldScore); System.IO.File.Delete($"data/replays/{oldScore.ReplayMd5}"); await DbScore.InsertScore(_ctx, dbScore); } else if (oldScore == null) { await DbScore.InsertScore(_ctx, dbScore); } else { System.IO.File.Delete($"data/replays/{oldScore.ReplayMd5}"); } var newlb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbScore.ScoreOwner); newlb.IncreasePlaycount(dbScore.PlayMode); newlb.IncreaseScore((ulong) dbScore.TotalScore, true, dbScore.PlayMode); newlb.IncreaseScore((ulong) dbScore.TotalScore, false, dbScore.PlayMode); newlb.UpdatePp(_ctx, dbScore.PlayMode); await newlb.SaveChanges(_ctx); var newStdPos = newlb.GetPosition(_ctx, dbScore.PlayMode); newAcc = newlb.GetAccuracy(_ctx, dbScore.PlayMode); var newScore = await DbScore.GetLatestScore(_ctx, dbScore); var set = await _pisstaube.FetchBeatmapSetAsync(dbScore.FileMd5); var bm = set?.ChildrenBeatmaps.First(x => x.FileMD5 == dbScore.FileMd5) ?? new Beatmap(); ulong oldRankedScore; ulong newRankedScore; double oldPp; double newPp; switch (dbScore.PlayMode) { case PlayMode.Osu: oldRankedScore = oldLb.RankedScoreOsu; newRankedScore = newlb.RankedScoreOsu; oldPp = oldLb.PerformancePointsOsu; newPp = newlb.PerformancePointsOsu; break; case PlayMode.Taiko: oldRankedScore = oldLb.RankedScoreTaiko; newRankedScore = newlb.RankedScoreTaiko; oldPp = oldLb.PerformancePointsTaiko; newPp = newlb.PerformancePointsTaiko; break; case PlayMode.Ctb: oldRankedScore = oldLb.RankedScoreCtb; newRankedScore = newlb.RankedScoreCtb; oldPp = oldLb.PerformancePointsCtb; newPp = newlb.PerformancePointsCtb; break; case PlayMode.Mania: oldRankedScore = oldLb.RankedScoreMania; newRankedScore = newlb.RankedScoreMania; oldPp = oldLb.PerformancePointsMania; newPp = newlb.PerformancePointsMania; break; default: return Ok(""); } var newScorePosition = newScore != null ? await newScore.Position(_ctx) : 0; var oldScorePosition = oldScore != null ? await oldScore.Position(_ctx) : 0; if (newScorePosition == 1) _sora.SendMessage( $"[http://{_config.Server.ScreenShotHostname}/{dbScore.ScoreOwner.Id} {dbScore.ScoreOwner.UserName}] " + $"has reached #1 on [https://osu.ppy.sh/b/{bm.BeatmapID} {set?.Title} [{bm.DiffName}]] " + $"using {ModUtil.ToString(newScore.Mods)} " + $"Good job! +{newScore.PerformancePoints:F}PP", "#announce", false ); Logger.Info( $"{LCol.RED}{dbScore.ScoreOwner.UserName}", $"{LCol.PURPLE}( {dbScore.ScoreOwner.Id} ){LCol.WHITE}", $"has just submitted a Score! he earned {LCol.BLUE}{newScore?.PerformancePoints:F}PP", $"{LCol.WHITE}with an Accuracy of {LCol.RED}{newScore?.Accuracy * 100:F}", $"{LCol.WHITE}on {LCol.YELLOW}{set?.Title} [{bm.DiffName}]", $"{LCol.WHITE}using {LCol.BLUE}{ModUtil.ToString(newScore?.Mods ?? Mod.None)}" ); var bmChart = new Chart( "beatmap", "Beatmap Ranking", $"https://osu.ppy.sh/b/{bm.BeatmapID}", oldScorePosition, newScorePosition, oldScore?.MaxCombo ?? 0, newScore?.MaxCombo ?? 0, oldScore?.Accuracy * 100 ?? 0, newScore?.Accuracy * 100 ?? 0, (ulong) (oldScore?.TotalScore ?? 0), (ulong) (newScore?.TotalScore ?? 0), oldScore?.PerformancePoints ?? 0, newScore?.PerformancePoints ?? 0, newScore?.Id ?? 0 ); var overallChart = new Chart( "overall", "Global Ranking", $"https://osu.ppy.sh/u/{dbUser.Id}", (int) oldStdPos, (int) newStdPos, 0, 0, oldAcc * 100, newAcc * 100, oldRankedScore, newRankedScore, oldPp, newPp, newScore?.Id ?? 0, AchievementProcessor.ProcessAchievements( _ctx, dbScore.ScoreOwner, score, bm, set, oldLb, newlb ) ); pr["LB"] = newlb; pr.Stats.Accuracy = (float) newlb.GetAccuracy(_ctx, score.PlayMode); pr.Stats.Position = newlb.GetPosition(_ctx, score.PlayMode); switch (score.PlayMode) { case PlayMode.Osu: pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsOsu; pr.Stats.TotalScore = (ushort) newlb.TotalScoreOsu; pr.Stats.RankedScore = (ushort) newlb.RankedScoreOsu; pr.Stats.PlayCount = (ushort) newlb.PlayCountOsu; break; case PlayMode.Taiko: pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsTaiko; pr.Stats.TotalScore = (ushort) newlb.TotalScoreTaiko; pr.Stats.RankedScore = (ushort) newlb.RankedScoreTaiko; pr.Stats.PlayCount = (ushort) newlb.PlayCountTaiko; break; case PlayMode.Ctb: pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsCtb; pr.Stats.TotalScore = (ushort) newlb.TotalScoreCtb; pr.Stats.RankedScore = (ushort) newlb.RankedScoreCtb; pr.Stats.PlayCount = (ushort) newlb.PlayCountCtb; break; case PlayMode.Mania: pr.Stats.PerformancePoints = (ushort) newlb.PerformancePointsMania; pr.Stats.TotalScore = (ushort) newlb.TotalScoreMania; pr.Stats.RankedScore = (ushort) newlb.RankedScoreMania; pr.Stats.PlayCount = (ushort) newlb.PlayCountMania; break; } // Send to other People await _ev.RunEvent( EventType.BanchoUserStatsRequest, new BanchoUserStatsRequestArgs {UserIds = new List<int> {score.Id}, Pr = pr} ); // Send to self await _ev.RunEvent( EventType.BanchoSendUserStatus, new BanchoSendUserStatusArgs {Status = pr.Status, Pr = pr} ); return Ok( $"beatmapId:{bm.BeatmapID}|beatmapSetId:{bm.ParentSetID}|beatmapPlaycount:0|beatmapPasscount:0|approvedDate:\n\n" + bmChart.ToOsuString() + "\n" + overallChart.ToOsuString() ); }
public async Task <IActionResult> GetScoreResult( [FromQuery(Name = "v")] ScoreboardType type, [FromQuery(Name = "c")] string fileMd5, [FromQuery(Name = "f")] string f, [FromQuery(Name = "m")] PlayMode playMode, [FromQuery(Name = "i")] int i, [FromQuery(Name = "mods")] Mod mods, [FromQuery(Name = "us")] string us, [FromQuery(Name = "ha")] string pa, [FromServices] IServiceProvider serviceProvider, [FromServices] SoraDbContext ctx, [FromServices] DbContextPool <SoraDbContext> ctxPool, [FromServices] Pisstaube pisstaube, [FromServices] Cache cache) { try { var dbUser = await DbUser.GetDbUser(ctx, us); if (dbUser?.IsPassword(pa) != true) { return(Ok("error: pass")); } var cacheHash = Hex.ToHex( Crypto.GetMd5( $"{fileMd5}{playMode}{mods}{type}{dbUser.Id}{dbUser.UserName}" ) ); if (cache.TryGet($"sora:Scoreboards:{cacheHash}", out string cachedData)) { return(Ok(cachedData)); } var scores = DbScore.GetScores(ctx, fileMd5, dbUser, playMode, type == ScoreboardType.Friends, type == ScoreboardType.Country, type == ScoreboardType.Mods, mods); BeatmapSet set = null; DbScore ownScore = null; var beatmap = DbBeatmap.GetBeatmap(ctx, fileMd5); if (beatmap == null) { var apiSet = await pisstaube.FetchBeatmapSetAsync(fileMd5); if (apiSet == null) { goto JustContinue; } var beatmaps = DbBeatmap.FromBeatmapSet(apiSet).ToList(); var beatmapChecksums = beatmaps.Select(s => s.FileMd5); var dbBeatmaps = ctx.Beatmaps.Where(rset => beatmapChecksums.Any(lFileMd5 => rset.FileMd5 == lFileMd5)) .ToList(); var pool = serviceProvider.GetRequiredService <DbContextPool <SoraDbContext> >(); Task.WaitAll(beatmaps.Select(rawBeatmap => Task.Run(async() => { var context = pool.Rent(); try { var dbBeatmap = dbBeatmaps.FirstOrDefault(s => s.FileMd5 == rawBeatmap.FileMd5); if (dbBeatmap != null && (dbBeatmap.Flags & DbBeatmapFlags.RankedFreeze) != 0) { rawBeatmap.RankedStatus = dbBeatmap.RankedStatus; rawBeatmap.Flags = dbBeatmap.Flags; } context.Beatmaps.AddOrUpdate(rawBeatmap); await context.SaveChangesAsync(); } finally { pool.Return(context); } })).ToArray()); beatmap = beatmaps.FirstOrDefault(s => s.FileMd5 == fileMd5); } await foreach (var score in DbScore.GetScores(ctx, fileMd5, dbUser, playMode, false, false, false, mods, true)) { ownScore = score; break; } if (beatmap != null) { set = new BeatmapSet { SetID = beatmap.Id, Artist = beatmap.Artist, Title = beatmap.Title, RankedStatus = beatmap.RankedStatus, ChildrenBeatmaps = new List <Beatmap> { new Beatmap { FileMD5 = beatmap.FileMd5, DiffName = beatmap.DiffName, ParentSetID = beatmap.SetId, BeatmapID = beatmap.Id, Mode = beatmap.PlayMode, }, }, } } ; JustContinue: var sboard = new Scoreboard(set?.ChildrenBeatmaps.FirstOrDefault(bm => bm.FileMD5 == fileMd5), set, scores, ownScore); cache.Set($"sora:Scoreboards:{cacheHash}", cachedData = await sboard.ToOsuString(ctxPool), TimeSpan.FromSeconds(30)); return(Ok(cachedData)); } catch (Exception ex) { Logger.Err(ex); return(Ok("Failed")); } }