diff --git a/ProjectPlugins/GethPlugin/EthTokenExtensions.cs b/ProjectPlugins/GethPlugin/EthTokenExtensions.cs index f9a9d8a..d85533a 100644 --- a/ProjectPlugins/GethPlugin/EthTokenExtensions.cs +++ b/ProjectPlugins/GethPlugin/EthTokenExtensions.cs @@ -28,7 +28,13 @@ public override string ToString() { - return $"{Eth} Eth"; + var weiOnly = Wei % TokensIntExtensions.WeiPerEth; + + var tokens = new List(); + if (Eth > 0) tokens.Add($"{Eth} Eth"); + if (weiOnly > 0) tokens.Add($"{weiOnly} Wei"); + + return string.Join(" + ", tokens); } } diff --git a/Tools/BiblioTech/AdminChecker.cs b/Tools/BiblioTech/AdminChecker.cs index def2870..00e7b6a 100644 --- a/Tools/BiblioTech/AdminChecker.cs +++ b/Tools/BiblioTech/AdminChecker.cs @@ -27,9 +27,9 @@ namespace BiblioTech return channel.Id == Program.Config.AdminChannelId; } - public ISocketMessageChannel GetAdminChannel() + public async Task SendInAdminChannel(string msg) { - return adminChannel; + await adminChannel.SendMessageAsync(msg); } public void SetAdminChannel(ISocketMessageChannel adminChannel) diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index 44695fd..a586471 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -1,6 +1,7 @@ using Discord.WebSocket; using BiblioTech.Options; using Discord; +using k8s.KubeConfigModels; namespace BiblioTech { @@ -25,16 +26,13 @@ namespace BiblioTech catch (Exception ex) { var msg = "Failed with exception: " + ex; - if (IsInAdminChannel(command)) - { - await command.FollowupAsync(msg.Substring(0, Math.Min(1900, msg.Length))); - } - else - { - await command.FollowupAsync("Something failed while trying to do that...", ephemeral: true); - await Program.AdminChecker.GetAdminChannel().SendMessageAsync(msg); - } Program.Log.Error(msg); + + if (!IsInAdminChannel(command)) + { + await command.FollowupAsync("Something failed while trying to do that... (error details posted in admin channel)", ephemeral: true); + } + await Program.AdminChecker.SendInAdminChannel(msg); } } @@ -62,5 +60,20 @@ namespace BiblioTech if (IsSenderAdmin(context.Command) && targetUser != null) return targetUser; return context.Command.User; } + + protected string Mention(SocketUser user) + { + return Mention(user.Id); + } + + protected string Mention(IUser user) + { + return Mention(user.Id); + } + + protected string Mention(ulong userId) + { + return $"<@{userId}>"; + } } } diff --git a/Tools/BiblioTech/Commands/GetBalanceCommand.cs b/Tools/BiblioTech/Commands/GetBalanceCommand.cs index 9454c79..8d2460d 100644 --- a/Tools/BiblioTech/Commands/GetBalanceCommand.cs +++ b/Tools/BiblioTech/Commands/GetBalanceCommand.cs @@ -28,6 +28,7 @@ namespace BiblioTech.Commands if (addr == null) { await context.Followup($"No address has been set for this user. Please use '/{userAssociateCommand.Name}' to set it first."); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(userId)} used '/{Name}' but address has not been set."); return; } diff --git a/Tools/BiblioTech/Commands/MintCommand.cs b/Tools/BiblioTech/Commands/MintCommand.cs index d3d2451..b5cb933 100644 --- a/Tools/BiblioTech/Commands/MintCommand.cs +++ b/Tools/BiblioTech/Commands/MintCommand.cs @@ -28,6 +28,7 @@ namespace BiblioTech.Commands if (addr == null) { await context.Followup($"No address has been set for this user. Please use '/{userAssociateCommand.Name}' to set it first."); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(userId)} used '/{Name}' but address has not been set."); return; } @@ -42,9 +43,17 @@ namespace BiblioTech.Commands mintedTokens = ProcessTokens(contracts, addr, report); }); + var reportLine = string.Join(Environment.NewLine, report); Program.UserRepo.AddMintEventForUser(userId, addr, sentEth, mintedTokens); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(userId)} used '/{Name}' successfully. ({reportLine})"); - await context.Followup(string.Join(Environment.NewLine, report)); + await context.Followup(reportLine); + } + + private string Format(Transaction? transaction) + { + if (transaction == null) return "-"; + return transaction.ToString(); } private Transaction? ProcessTokens(ICodexContracts contracts, EthAddress addr, List report) diff --git a/Tools/BiblioTech/Commands/UserAssociateCommand.cs b/Tools/BiblioTech/Commands/UserAssociateCommand.cs index 81e4646..1fc6d63 100644 --- a/Tools/BiblioTech/Commands/UserAssociateCommand.cs +++ b/Tools/BiblioTech/Commands/UserAssociateCommand.cs @@ -1,4 +1,8 @@ using BiblioTech.Options; +using Discord; +using GethPlugin; +using k8s.KubeConfigModels; +using NBitcoin.Secp256k1; namespace BiblioTech.Commands { @@ -23,30 +27,56 @@ namespace BiblioTech.Commands protected override async Task Invoke(CommandContext context) { var user = GetUserFromCommand(optionalUser, context); - var data = await ethOption.Parse(context); - if (data == null) return; + var newAddress = await ethOption.Parse(context); + if (newAddress == null) return; var currentAddress = Program.UserRepo.GetCurrentAddressForUser(user); if (currentAddress != null && !IsSenderAdmin(context.Command)) { await context.Followup($"You've already set your Ethereum address to {currentAddress}."); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' but already has an address set. ({currentAddress})"); return; } - var result = Program.UserRepo.AssociateUserWithAddress(user, data); - if (result) + var result = Program.UserRepo.AssociateUserWithAddress(user, newAddress); + switch (result) { - await context.Followup(new string[] - { + case SetAddressResponse.OK: + await ResponseOK(context, user, newAddress); + break; + case SetAddressResponse.AddressAlreadyInUse: + await ResponseAlreadyUsed(context, user, newAddress); + break; + case SetAddressResponse.CreateUserFailed: + await ResponseCreateUserFailed(context, user); + break; + default: + throw new Exception("Unknown SetAddressResponse mode"); + } + } + + private async Task ResponseCreateUserFailed(CommandContext context, IUser user) + { + await context.Followup("Internal error. Error details sent to admin."); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' but failed to create new user."); + } + + private async Task ResponseAlreadyUsed(CommandContext context, IUser user, EthAddress newAddress) + { + await context.Followup("This address is already in use by another user."); + await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' but the provided address is already in use by another user. (address: {newAddress})"); + } + + private async Task ResponseOK(CommandContext context, IUser user, GethPlugin.EthAddress newAddress) + { + await context.Followup(new string[] +{ "Done! Thank you for joining the test net!", - "By default, the bot will @-mention you with test-net reward related notifications.", + "By default, the bot will @-mention you with test-net related notifications.", $"You can enable/disable this behavior with the '/{notifyCommand.Name}' command." - }); - } - else - { - await context.Followup("That didn't work."); - } +}); + + await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' successfully. ({newAddress})"); } } } diff --git a/Tools/BiblioTech/Transaction.cs b/Tools/BiblioTech/Transaction.cs index a19814a..be2c828 100644 --- a/Tools/BiblioTech/Transaction.cs +++ b/Tools/BiblioTech/Transaction.cs @@ -10,5 +10,11 @@ public T TokenAmount { get; } public string TransactionHash { get; } + + public override string ToString() + { + if (TokenAmount == null) return "NULL"; + return TokenAmount.ToString()!; + } } } diff --git a/Tools/BiblioTech/UserRepo.cs b/Tools/BiblioTech/UserRepo.cs index 7cd03fd..35d8ec8 100644 --- a/Tools/BiblioTech/UserRepo.cs +++ b/Tools/BiblioTech/UserRepo.cs @@ -10,7 +10,7 @@ namespace BiblioTech private readonly object repoLock = new object(); private readonly Dictionary cache = new Dictionary(); - public bool AssociateUserWithAddress(IUser user, EthAddress address) + public SetAddressResponse AssociateUserWithAddress(IUser user, EthAddress address) { lock (repoLock) { @@ -134,18 +134,19 @@ namespace BiblioTech return null; } - private bool SetUserAddress(IUser user, EthAddress? address) + private SetAddressResponse SetUserAddress(IUser user, EthAddress? address) { if (GetUserDataForAddress(address) != null) { - return false; + return SetAddressResponse.AddressAlreadyInUse; } var userData = GetOrCreate(user); + if (userData == null) return SetAddressResponse.CreateUserFailed; userData.CurrentAddress = address; userData.AssociateEvents.Add(new UserAssociateAddressEvent(DateTime.UtcNow, address)); SaveUserData(userData); - return true; + return SetAddressResponse.OK; } private void SetUserNotification(IUser user, bool notifyEnabled) @@ -245,4 +246,11 @@ namespace BiblioTech } } } + + public enum SetAddressResponse + { + OK, + AddressAlreadyInUse, + CreateUserFailed + } }