Update: Bot reports interactions in admin channel

This commit is contained in:
Ben 2024-09-30 10:59:22 +02:00
parent f801cb082e
commit 200de1d7f7
No known key found for this signature in database
GPG Key ID: 0F16E812E736C24B
8 changed files with 103 additions and 30 deletions

View File

@ -28,7 +28,13 @@
public override string ToString() public override string ToString()
{ {
return $"{Eth} Eth"; var weiOnly = Wei % TokensIntExtensions.WeiPerEth;
var tokens = new List<string>();
if (Eth > 0) tokens.Add($"{Eth} Eth");
if (weiOnly > 0) tokens.Add($"{weiOnly} Wei");
return string.Join(" + ", tokens);
} }
} }

View File

@ -27,9 +27,9 @@ namespace BiblioTech
return channel.Id == Program.Config.AdminChannelId; 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) public void SetAdminChannel(ISocketMessageChannel adminChannel)

View File

@ -1,6 +1,7 @@
using Discord.WebSocket; using Discord.WebSocket;
using BiblioTech.Options; using BiblioTech.Options;
using Discord; using Discord;
using k8s.KubeConfigModels;
namespace BiblioTech namespace BiblioTech
{ {
@ -25,16 +26,13 @@ namespace BiblioTech
catch (Exception ex) catch (Exception ex)
{ {
var msg = "Failed with 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); 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; if (IsSenderAdmin(context.Command) && targetUser != null) return targetUser;
return context.Command.User; 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}>";
}
} }
} }

View File

@ -28,6 +28,7 @@ namespace BiblioTech.Commands
if (addr == null) if (addr == null)
{ {
await context.Followup($"No address has been set for this user. Please use '/{userAssociateCommand.Name}' to set it first."); 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; return;
} }

View File

@ -28,6 +28,7 @@ namespace BiblioTech.Commands
if (addr == null) if (addr == null)
{ {
await context.Followup($"No address has been set for this user. Please use '/{userAssociateCommand.Name}' to set it first."); 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; return;
} }
@ -42,9 +43,17 @@ namespace BiblioTech.Commands
mintedTokens = ProcessTokens(contracts, addr, report); mintedTokens = ProcessTokens(contracts, addr, report);
}); });
var reportLine = string.Join(Environment.NewLine, report);
Program.UserRepo.AddMintEventForUser(userId, addr, sentEth, mintedTokens); 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<T>(Transaction<T>? transaction)
{
if (transaction == null) return "-";
return transaction.ToString();
} }
private Transaction<TestToken>? ProcessTokens(ICodexContracts contracts, EthAddress addr, List<string> report) private Transaction<TestToken>? ProcessTokens(ICodexContracts contracts, EthAddress addr, List<string> report)

View File

@ -1,4 +1,8 @@
using BiblioTech.Options; using BiblioTech.Options;
using Discord;
using GethPlugin;
using k8s.KubeConfigModels;
using NBitcoin.Secp256k1;
namespace BiblioTech.Commands namespace BiblioTech.Commands
{ {
@ -23,30 +27,56 @@ namespace BiblioTech.Commands
protected override async Task Invoke(CommandContext context) protected override async Task Invoke(CommandContext context)
{ {
var user = GetUserFromCommand(optionalUser, context); var user = GetUserFromCommand(optionalUser, context);
var data = await ethOption.Parse(context); var newAddress = await ethOption.Parse(context);
if (data == null) return; if (newAddress == null) return;
var currentAddress = Program.UserRepo.GetCurrentAddressForUser(user); var currentAddress = Program.UserRepo.GetCurrentAddressForUser(user);
if (currentAddress != null && !IsSenderAdmin(context.Command)) if (currentAddress != null && !IsSenderAdmin(context.Command))
{ {
await context.Followup($"You've already set your Ethereum address to {currentAddress}."); 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; return;
} }
var result = Program.UserRepo.AssociateUserWithAddress(user, data); var result = Program.UserRepo.AssociateUserWithAddress(user, newAddress);
if (result) 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!", "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." $"You can enable/disable this behavior with the '/{notifyCommand.Name}' command."
}); });
}
else await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' successfully. ({newAddress})");
{
await context.Followup("That didn't work.");
}
} }
} }
} }

View File

@ -10,5 +10,11 @@
public T TokenAmount { get; } public T TokenAmount { get; }
public string TransactionHash { get; } public string TransactionHash { get; }
public override string ToString()
{
if (TokenAmount == null) return "NULL";
return TokenAmount.ToString()!;
}
} }
} }

View File

@ -10,7 +10,7 @@ namespace BiblioTech
private readonly object repoLock = new object(); private readonly object repoLock = new object();
private readonly Dictionary<ulong, UserData> cache = new Dictionary<ulong, UserData>(); private readonly Dictionary<ulong, UserData> cache = new Dictionary<ulong, UserData>();
public bool AssociateUserWithAddress(IUser user, EthAddress address) public SetAddressResponse AssociateUserWithAddress(IUser user, EthAddress address)
{ {
lock (repoLock) lock (repoLock)
{ {
@ -134,18 +134,19 @@ namespace BiblioTech
return null; return null;
} }
private bool SetUserAddress(IUser user, EthAddress? address) private SetAddressResponse SetUserAddress(IUser user, EthAddress? address)
{ {
if (GetUserDataForAddress(address) != null) if (GetUserDataForAddress(address) != null)
{ {
return false; return SetAddressResponse.AddressAlreadyInUse;
} }
var userData = GetOrCreate(user); var userData = GetOrCreate(user);
if (userData == null) return SetAddressResponse.CreateUserFailed;
userData.CurrentAddress = address; userData.CurrentAddress = address;
userData.AssociateEvents.Add(new UserAssociateAddressEvent(DateTime.UtcNow, address)); userData.AssociateEvents.Add(new UserAssociateAddressEvent(DateTime.UtcNow, address));
SaveUserData(userData); SaveUserData(userData);
return true; return SetAddressResponse.OK;
} }
private void SetUserNotification(IUser user, bool notifyEnabled) private void SetUserNotification(IUser user, bool notifyEnabled)
@ -245,4 +246,11 @@ namespace BiblioTech
} }
} }
} }
public enum SetAddressResponse
{
OK,
AddressAlreadyInUse,
CreateUserFailed
}
} }