Ejemplo n.º 1
0
        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()) }));
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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...
        }
Ejemplo n.º 4
0
        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));
        }
Ejemplo n.º 5
0
        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))));
        }
Ejemplo n.º 6
0
        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
            }));
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        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;
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
Archivo: Me.cs Proyecto: Mempler/Sora
        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),
            }));
        }
Ejemplo n.º 11
0
        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);
        }
Ejemplo n.º 12
0
 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
         },
Ejemplo n.º 13
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()) }));
        }
Ejemplo n.º 14
0
        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();
            }
        }
Ejemplo n.º 15
0
        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);
            }
        }
Ejemplo n.º 16
0
        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");
            }
        }
Ejemplo n.º 17
0
        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()
            );
        }
Ejemplo n.º 18
0
        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"));
            }
        }