internal override void Handle(ClientSession session, JsonSendPmIncomingMessage message) { if (session.IsGuest) { return; } if (message.Message.Length > 0) { UserManager.TryGetUserDataByNameAsync(message.ReceiverUsername).ContinueWith((task) => { PlayerUserData userData = task.Result; if (userData != null) { PrivateMessageManager.SendTextPrivateMessageAsync(userData.Id, session.UserData.Id, message.Title, message.Message); } else { session.SendPacket(new AlertOutgoingMessage("User was not found!")); } }); } else { session.SendPacket(new AlertOutgoingMessage("What if you typed message before sending?")); } }
public void OnCommand(ICommandExecutor executor, string label, ReadOnlySpan <string> args) { if (args.Length == 2) { PlayerUserData playerUserData = UserManager.TryGetUserDataByNameAsync(args[0]).Result; if (playerUserData != null) { if (!uint.TryParse(args[1], out uint amount)) { executor.SendMessage("The amount must be unsigned integer"); return; } if (PlatformRacing3Server.ClientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveBonusExp(amount); } else { playerUserData.GiveBonusExp(amount); } } else { executor.SendMessage($"Unable to find user named {args[0]}"); } } else { executor.SendMessage("Usage: /givebonusexp [user] [amount]"); } }
public async Task <object> IsLoggedInAsync([FromQuery] string id, [FromForm] string gameId) { if (id == "IsLoggedIn" && gameId == "f1c25e3bd3523110394b5659c68d8092") //Sparkworks { uint userId = this.HttpContext.IsAuthenicatedPr3User(); if (userId > 0) { PlayerUserData playerUserData = await UserManager.TryGetUserDataByIdAsync(userId); if (playerUserData != null) { return(new IsLoggedInResponse(userId, playerUserData.Username)); } else { return(new DataAccessErrorResponse("We were unable to load your user data")); } } else { return(new IsLoggedInResponse()); } } return(this.BadRequest()); }
public async Task <IDataAccessDataResponse> GetResponse(HttpContext httpContext, XDocument xml) { uint userId = httpContext.IsAuthenicatedPr3User(); if (userId > 0) { XElement data = xml.Element("Params"); if (data != null) { uint levelId = (uint?)data.Element("p_level_id") ?? throw new DataAccessProcedureMissingData(); uint levelVersion = (uint?)data.Element("p_level_version") ?? throw new DataAccessProcedureMissingData(); string recordRun = (string)data.Element("p_recorded_run") ?? throw new DataAccessProcedureMissingData(); int finishTime = (int?)data.Element("p_finish_time") ?? throw new DataAccessProcedureMissingData(); CampaignRun campaignRun = null; using (MemoryStream compressedMemoryStream = new MemoryStream(Convert.FromBase64String(recordRun))) { using (InflaterInputStream inflater = new InflaterInputStream(compressedMemoryStream)) { using (MemoryStream uncompressedMemoryStream = new MemoryStream()) { inflater.CopyTo(uncompressedMemoryStream); campaignRun = JsonConvert.DeserializeObject <CampaignRun>(Encoding.UTF8.GetString(uncompressedMemoryStream.ToArray())); } } } if (campaignRun != null) { PlayerUserData playerUserData = await UserManager.TryGetUserDataByIdAsync(userId); if (campaignRun.Username != playerUserData.Username) { return(new DataAccessErrorResponse("Invalid username")); } await CampaignManager.SaveCampaignRun(userId, levelId, levelVersion, recordRun, finishTime); return(new DataAccessSaveCampaignRun3Response()); } else { throw new DataAccessProcedureMissingData(); } } else { throw new DataAccessProcedureMissingData(); } } else { return(new DataAccessErrorResponse("You are not logged in!")); } }
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] //Dynamic get public async Task <ContentResult> GetAsync([FromQuery] string code) { uint userId = this.HttpContext.IsAuthenicatedPr3User(); if (userId <= 0) { return(this.Content(@"Please login to the client in the browser! Or <a href=""https://pr3hub.com/login"">click here</a> to do so without the Flash client.", "text/html")); } if (string.IsNullOrWhiteSpace(code)) { PlayerUserData playerUserData = await UserManager.TryGetUserDataByIdAsync(userId); return(this.Content($@"Are you sure that you want to link your {playerUserData.Username} account to your Discord account? <a href=""https://discordapp.com/api/oauth2/authorize?response_type=code&client_id={Program.Config.DiscordClientId}&scope=identify"">Click here to proceed</a>", "text/html")); } DiscordTokenResponse tokenResponse = await RequestTokenAsync(code); if (!string.IsNullOrWhiteSpace(tokenResponse.Error)) { return(this.Content($"Requesting discord token returned the following error: {tokenResponse.ErrorReason}")); } DiscordUserResponse user = await GetUser(tokenResponse); if (user.Code != null) { return(this.Content($"Requesting discord profile resulted to the following error: {user.Message}")); } uint linkedToAccount = await UserManager.HasDiscordLinkage(userId, user.Id); if (linkedToAccount == 0) { if (!await UserManager.TryInsertDiscordLinkage(userId, user.Id)) { return(this.Content("Oops, something went wrong!")); } return(this.Content("All ok! :sunglasses:")); } else { if (linkedToAccount == userId) { return(this.Content("This account has already been linked to Discord account!")); } else { return(this.Content("This Discord account has already been linked to different account!")); } }
public async Task <string> PostAsync([FromForm] string email, [FromForm] string token, [FromForm] string password) { //Password check if (string.IsNullOrWhiteSpace(password)) { return("Please enter password"); } else if (password.Length < RegisterController.PASSWORD_MIN_LENGTH) { return($"Password must be at least {RegisterController.PASSWORD_MIN_LENGTH} chars long"); } PlayerUserData player = await UserManager.TryGetUserDataByEmailAsync(email); if (player == null) { return("Invalid token"); } string hashedToken; using (SHA512 sha = SHA512.Create()) { hashedToken = Convert.ToBase64String(sha.ComputeHash(WebEncoders.Base64UrlDecode(token))); } uint userId = await UserManager.TryGetForgotPasswordToken(hashedToken); if (userId == 0 || userId != player.Id) { return("Invalid token"); } if (!await UserManager.TryConsumePasswordToken(hashedToken, password, this.HttpContext.Connection.RemoteIpAddress)) { return("Error, try again"); } using (MailMessage mail = new()) { mail.To.Add(email); mail.Subject = $"Platform Racing 3 - {player.Username} - Security Alert"; mail.From = new MailAddress(Program.Config.SmtpUser); mail.Body = $"Your account {player.Username} password was reset! If you did not do this change your password immediately!"; Program.SmtpClient.Send(mail); } return("Password has been reset! Start the grind :sunglasses:"); }
public async Task <IDataAccessDataResponse> GetResponseAsync(HttpContext httpContext, XDocument xml) { uint userId = httpContext.IsAuthenicatedPr3User(); if (userId > 0) { XElement data = xml.Element("Params"); if (data != null) { uint levelId = (uint?)data.Element("p_level_id") ?? throw new DataAccessProcedureMissingData(); uint levelVersion = (uint?)data.Element("p_level_version") ?? throw new DataAccessProcedureMissingData(); string recordRun = (string)data.Element("p_recorded_run") ?? throw new DataAccessProcedureMissingData(); int finishTime = (int?)data.Element("p_finish_time") ?? throw new DataAccessProcedureMissingData(); CampaignRun campaignRun = CampaignRun.FromCompressed(recordRun); if (campaignRun != null) { PlayerUserData playerUserData = await UserManager.TryGetUserDataByIdAsync(userId); if (campaignRun.Username != playerUserData.Username) { return(new DataAccessErrorResponse("Invalid username")); } await CampaignManager.SaveCampaignRunAsync(userId, levelId, levelVersion, recordRun, finishTime); return(new DataAccessSaveCampaignRun3Response()); } else { throw new DataAccessProcedureMissingData(); } } else { throw new DataAccessProcedureMissingData(); } } else { return(new DataAccessErrorResponse("You are not logged in!")); } }
public void OnCommand(ICommandExecutor executor, string label, ReadOnlySpan <string> args) { if (args.Length >= 2 && args.Length <= 3) { PlayerUserData playerUserData = UserManager.TryGetUserDataByNameAsync(args[0]).Result; if (playerUserData != null) { Hat hat; if (uint.TryParse(args[1], out uint hatId)) { hat = (Hat)hatId; } else if (!Enum.TryParse(args[1], ignoreCase: true, out hat)) { executor.SendMessage($"Unable to find part with name {args[1]}"); } bool temp = false; if (args.Length >= 3) { bool.TryParse(args[2], out temp); } if (this.clientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveHat(hat, temp); } else if (!temp) { UserManager.GiveHat(playerUserData.Id, hat); } } else { executor.SendMessage($"Unable to find user named {args[0]}"); } } else { executor.SendMessage("Usage: /givehat [user] [id/name] [temporaly(false)]"); } }
public Task GetOnlinePlayersCount() { return(DoAsync()); async Task DoAsync() { uint userId = await UserManager.HasDiscordLinkage(this.Context.User.Id); if (userId == 0) { await this.ReplyAsync("Well bruh, u ain't got ur Discord linkage"); return; } PlayerUserData userData = await UserManager.TryGetUserDataByIdAsync(userId); EmbedBuilder embed = new(); BuildHats(embed, userData); BuildParts(embed, userData); await this.ReplyAsync(message : this.Context.User.Mention, embed : embed.Build());
public async Task <string> PostAsync([FromForm] string email) { PlayerUserData player = await UserManager.TryGetUserDataByEmailAsync(email); if (player != null) { if (!await UserManager.CanSendPasswordToken(player.Id)) { return("Please request new password reset token after half an hour!"); } //Generate 512 bit random token byte[] token = new byte[512 / 8]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { rng.GetNonZeroBytes(token.AsSpan(start: 0, length: 32)); } using (SHA256 sha = SHA256.Create()) { byte[] content = Encoding.UTF8.GetBytes(player.Id.ToString() + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + email); if (!sha.TryComputeHash(content, token.AsSpan(start: 32, length: 32), out int bytesWritten) || bytesWritten != 32) { //Should not happen, right? return("Something went wrong when trying to generate the token! Try again!"); } } byte[] tokenHash; //Generate hash of the token for database using (SHA512 sha = SHA512.Create()) { tokenHash = sha.ComputeHash(token); } bool result = await UserManager.TryCreateForgotPasswordToken(player.Id, Convert.ToBase64String(tokenHash), this.HttpContext.Connection.RemoteIpAddress); if (!result) { return("Something went wrong when trying to create the token! Try again!"); } using (MailMessage mail = new()) { mail.To.Add(email); mail.Subject = $"Platform Racing 3 - {player.Username} - Password Reset"; mail.From = new MailAddress(Program.Config.SmtpUser); mail.Body = $"You have requested password reset for your {player.Username} account! Use the following link to reset your password! https://pr3hub.com/resetpassword?email={email}&token={WebEncoders.Base64UrlEncode(token)}"; Program.SmtpClient.Send(mail); } return("We have sent you email!"); } else { return("User was not found!"); } }
public void OnCommand(ICommandExecutor executor, string label, ReadOnlySpan <string> args) { if (args.Length >= 3 && args.Length <= 4) { PlayerUserData playerUserData = UserManager.TryGetUserDataByNameAsync(args[0]).Result; if (playerUserData != null) { Part part; if (uint.TryParse(args[2], out uint partId)) { part = (Part)partId; } else if (!Enum.TryParse(args[2], ignoreCase: true, out part)) { executor.SendMessage($"Unable to find part with name {args[2]}"); } bool temp = false; if (args.Length >= 4) { bool.TryParse(args[3], out temp); } switch (args[1]) { case "head": { if (this.clientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveHead(part, temp); } else if (!temp) { UserManager.GiveHead(playerUserData.Id, part); } } break; case "body": { if (this.clientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveBody(part, temp); } else if (!temp) { UserManager.GiveBody(playerUserData.Id, part); } } break; case "feet": { if (this.clientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveFeet(part, temp); } else if (!temp) { UserManager.GiveFeet(playerUserData.Id, part); } } break; case "set": { if (this.clientManager.TryGetClientSessionByUserId(playerUserData.Id, out ClientSession session) && session.UserData != null) { session.UserData.GiveSet(part, temp); } else if (!temp) { UserManager.GiveSet(playerUserData.Id, part); } } break; } } else { executor.SendMessage($"Unable to find user named {args[0]}"); } } else { executor.SendMessage("Usage: /givepart [user] [head/body/feet/set] [id/name] [temporaly(false)]"); } }
public async Task <string> RegisterAsync([FromForm] string username, [FromForm] string password, [FromForm(Name = "retype_password")] string retypePassword, [FromForm] string email) { //Username check if (string.IsNullOrWhiteSpace(username)) { return("Please enter username"); } else if (username.Length < RegisterController.USERNAME_MIN_LENGTH) { return($"Username must be at least {RegisterController.USERNAME_MIN_LENGTH} char(s)"); } else if (username.Length > RegisterController.USERNAME_MAX_LENGTH) { return($"Username can't be longer than {RegisterController.USERNAME_MAX_LENGTH} char(s)"); } else if (!RegisterController.UsernameRegex.IsMatch(username)) { return("Username may only have characters are a-Z, 0-9, _ and -"); } //Password check if (string.IsNullOrWhiteSpace(password)) { return("Please enter password"); } else if (password.Length < RegisterController.PASSWORD_MIN_LENGTH) { return($"Password must be at least {RegisterController.PASSWORD_MIN_LENGTH} chars long"); } //Retype password check, this should be in client if (string.IsNullOrWhiteSpace(retypePassword)) { return("Please retype your password"); } else if (password != retypePassword) { return("Passwords don't match"); } //Email check if (string.IsNullOrWhiteSpace(email)) { return("Please enter email address, this can be used to recover your account"); } else if (email.Length > RegisterController.EMAIL_MAX_LENGTH) { return($"Email address can't be longer than {RegisterController.EMAIL_MAX_LENGTH} char(s)"); } else { try { new MailAddress(email); //TODO: Alternativly we could use regex } catch { return("Please enter valid email address"); } } Task <PlayerUserData> userByName = UserManager.TryGetUserDataByNameAsync(username); Task <PlayerUserData> userByEmail = UserManager.TryGetUserDataByEmailAsync(email); //This speeds up things so lets do this? //Check for dublicated username if (await userByName != null) { return("Username has been taken already!"); } //Check for dublicated email if (await userByEmail != null) { return("Email is linked to another user already!"); } PlayerUserData userData = await UserManager.TryCreateNewUserAsync(username, password, email, this.HttpContext.Connection.RemoteIpAddress); if (userData == null) { return("Critical error while trying to create user! Please try again! If this error keeps happening, please report it to the staff team"); } await this.HttpContext.LogOutPr3UserAsync(); //Log out to make sure authenicate passes successfully await this.HttpContext.AuthenicatePr3UserAsync(userData.Id); //Log in the user automatically after register return(string.Empty); //Everything is fine, empty requests represents that there was no issues, bad design }
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] //Dynamic get public async Task <ContentResult> GetAsync([FromQuery] string code) { uint userId = this.HttpContext.IsAuthenicatedPr3User(); if (userId > 0) { if (string.IsNullOrWhiteSpace(code)) { PlayerUserData playerUserData = await UserManager.TryGetUserDataByIdAsync(userId); return(this.Content($@"Are you sure you want to link your {playerUserData.Username} account to your Discord account? <a href=""https://discordapp.com/api/oauth2/authorize?response_type=code&client_id={Program.Config.DiscordClientId}&scope=identify"">Click here to proceed</a>", "text/html")); } else { DiscordTokenResponse tokenResponse = await RequestTokenAsync(); if (!string.IsNullOrWhiteSpace(tokenResponse.Error)) { return(this.Content($"Requesting discord token returned the following error: {tokenResponse.ErrorReason}")); } DiscordUserResponse user = await GetUser(); if (user.Code != null) { return(this.Content($"Requesting discord profile resulted to the following error: {user.Message}")); } uint linkedToAccount = await UserManager.HasDiscordLinkage(userId, user.Id); if (linkedToAccount == 0) { if (!await UserManager.TryInsertDiscordLinkage(userId, user.Id)) { return(this.Content("Oops, something went wrong!")); } return(this.Content("All ok! :sunglasses:")); } else { if (linkedToAccount == userId) { return(this.Content("This account has already been linked to Discord account!")); } else { return(this.Content("This Discord account has already been linked to different account!")); } } async Task <DiscordUserResponse> GetUser() { using (HttpClient httpClient = new()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(tokenResponse.TokenType, tokenResponse.AccessToken); using (HttpResponseMessage message = await httpClient.GetAsync(LinkDiscordController.DISCORD_API_ME)) { return(JsonConvert.DeserializeObject <DiscordUserResponse>(await message.Content.ReadAsStringAsync())); } } } async Task <DiscordTokenResponse> RequestTokenAsync() { IDictionary <string, string> values = new Dictionary <string, string>() { { "client_id", Program.Config.DiscordClientId }, { "client_secret", Program.Config.DiscordClientSecret }, { "grant_type", "authorization_code" }, { "code", code }, { "scope", "identify" }, }; using (HttpClient httpClient = new()) { using (FormUrlEncodedContent content = new(values)) { using (HttpResponseMessage message = await httpClient.PostAsync(LinkDiscordController.DISCORD_API_TOKEN, content)) { return(JsonConvert.DeserializeObject <DiscordTokenResponse>(await message.Content.ReadAsStringAsync())); } } } } } } else { return(this.Content("Please login to the client!")); } }
internal override void Handle(ClientSession session, JsonTokenLoginIncomingMessage message) { if (session.UpgradeClientStatus(ClientStatus.LoggedIn)) { RedisConnection.GetDatabase().StringGetAsync($"logintoken:{message.LoginToken}").ContinueWith((task) => { if (task.IsCompletedSuccessfully) { RedisValue result = task.Result; if (result.HasValue) { UserManager.TryGetUserDataByIdAsync((uint)result, false).ContinueWith((task_) => { if (task_.IsCompletedSuccessfully) { PlayerUserData result_ = task_.Result; if (result_ != null) { DatabaseConnection.NewAsyncConnection((dbConnection) => dbConnection.ReadDataAsync($"INSERT INTO base.users_logins(user_id, ip, data, type) VALUES({result_.Id}, {session.IPAddres}, {message.LoginToken}, 'server')").ContinueWith((task__) => { if (task__.IsCompletedSuccessfully) { session.UserData = result_; session.SendPacket(new LoginSuccessOutgoingMessage(session.SocketId, result_.Id, result_.Username, result_.Permissions, result_.GetVars("*"))); session.SendPacket(new FriendsAndIgnoredOutgoingMessage(result_.Friends, result_.Ignored)); PlatformRacing3Server.ClientManager.Add(session); //Race condition if (PlatformRacing3Server.ServerManager.TryGetServer(PlatformRacing3Server.ServerConfig.ServerId, out ServerDetails server)) { result_.SetServer(server); } } else if (task__.IsFaulted) { TokenLoginIncomingMessage.Logger.Error($"Failed to insert login", task.Exception); session.SendPacket(new LoginErrorOutgoingMessage("Critical error")); } })); } else { session.SendPacket(new LoginErrorOutgoingMessage("User data was not found")); } } else if (task_.IsFaulted) { session.SendPacket(new LoginErrorOutgoingMessage("Critical error while trying to load user data")); TokenLoginIncomingMessage.Logger.Error("Failed to load user data", task_.Exception); } else { session.SendPacket(new LoginErrorOutgoingMessage("Failed to load user data")); } }); } else { session.SendPacket(new LoginErrorOutgoingMessage("This login token is invalid")); } } else if (task.IsFaulted) { session.SendPacket(new LoginErrorOutgoingMessage("Critical error while trying to retrieve login token information")); TokenLoginIncomingMessage.Logger.Error("Failed to retieve login token", task.Exception); } else { session.SendPacket(new LoginErrorOutgoingMessage("Failed to retrieve login token information")); } }); } }
public Task GetOnlinePlayersCount() { return(DoAsync()); async Task DoAsync() { uint userId = await UserManager.HasDiscordLinkage(this.Context.User.Id); if (userId == 0) { await this.ReplyAsync("Well bruh, u ain't got ur Discord linkage"); return; } PlayerUserData userData = await UserManager.TryGetUserDataByIdAsync(userId); (int Count, int Max)hats = default; StringBuilder hatsWriter = new(); foreach (Hat hat in Enum.GetValues(typeof(Hat))) { if (hat == Hat.None || hat.IsStaffOnly()) { continue; } hats.Max++; hatsWriter.Append(hat.ToString()); hatsWriter.Append(" "); if (userData.HasHat(hat)) { hatsWriter.Append("✓"); hats.Count++; } else { hatsWriter.Append("✕"); } hatsWriter.AppendLine(); } (int Head, int Body, int Feet, int Max)parts = default; (int Head, int Body, int Feet, int Max)tournieParts = default; (StringBuilder Head, StringBuilder Body, StringBuilder Feet)partWriters = (new StringBuilder(), new StringBuilder(), new StringBuilder()); (StringBuilder Head, StringBuilder Body, StringBuilder Feet)tourniePartWriters = (new StringBuilder(), new StringBuilder(), new StringBuilder()); foreach (Part part in Enum.GetValues(typeof(Part))) { if (part == Part.None || part.IsStaffOnly()) { continue; } bool isTournie = part.IsTournamentPrize(); CountTo(ref !isTournie ? ref partWriters : ref tourniePartWriters, ref !isTournie ? ref parts : ref tournieParts); void CountTo(ref (StringBuilder Head, StringBuilder Body, StringBuilder Feet) writers, ref (int Head, int Body, int Feet, int Max) partCounter) { partCounter.Max++; AppendToAll(ref writers, part.ToString()); AppendToAll(ref writers, " "); if (userData.HasHead(part)) { writers.Head.Append("✓"); partCounter.Head++; } else { writers.Head.Append("✕"); } if (userData.HasBody(part)) { writers.Body.Append("✓"); partCounter.Body++; } else { writers.Body.Append("✕"); } if (userData.HasFeet(part)) { writers.Feet.Append("✓"); partCounter.Feet++; } else { writers.Feet.Append("✕"); } BreakAll(ref writers); } void AppendToAll(ref (StringBuilder Head, StringBuilder Body, StringBuilder Feet) writers, string value) { writers.Head.Append(value); writers.Body.Append(value); writers.Feet.Append(value); } void BreakAll(ref (StringBuilder Head, StringBuilder Body, StringBuilder Feet) writers) { writers.Head.AppendLine(); writers.Body.AppendLine(); writers.Feet.AppendLine(); } } EmbedBuilder embed = new(); embed.AddField(new EmbedFieldBuilder() { Name = $"Hats ({hats.Count}/{hats.Max})", Value = hatsWriter.ToString() }); embed.AddField(new EmbedFieldBuilder() { Name = $"Heads ({parts.Head}/{parts.Max})", Value = partWriters.Head.ToString(), IsInline = true }); embed.AddField(new EmbedFieldBuilder() { Name = $"Bodies ({parts.Body}/{parts.Max})", Value = partWriters.Body.ToString(), IsInline = true }); embed.AddField(new EmbedFieldBuilder() { Name = $"Feets ({parts.Feet}/{parts.Max})", Value = partWriters.Feet.ToString(), IsInline = true }); embed.AddField(new EmbedFieldBuilder() { Name = $"Tournie Heads ({tournieParts.Head}/{tournieParts.Max})", Value = tourniePartWriters.Head.ToString(), IsInline = true }); embed.AddField(new EmbedFieldBuilder() { Name = $"Tournie Bodies ({tournieParts.Body}/{tournieParts.Max})", Value = tourniePartWriters.Body.ToString(), IsInline = true }); embed.AddField(new EmbedFieldBuilder() { Name = $"Tournie Feets ({tournieParts.Feet}/{tournieParts.Max})", Value = tourniePartWriters.Feet.ToString(), IsInline = true }); await this.ReplyAsync(message : this.Context.User.Mention, embed : embed.Build()); } }