Merge branch 'feature/bot-upgrade'

This commit is contained in:
benbierens 2024-04-08 16:08:05 +02:00
commit 605004286a
No known key found for this signature in database
GPG Key ID: 877D2C2E09A22F3A
10 changed files with 170 additions and 12 deletions

View File

@ -4,6 +4,7 @@
{ {
public RewardUsersCommand[] Rewards { get; set; } = Array.Empty<RewardUsersCommand>(); public RewardUsersCommand[] Rewards { get; set; } = Array.Empty<RewardUsersCommand>();
public MarketAverage[] Averages { get; set; } = Array.Empty<MarketAverage>(); public MarketAverage[] Averages { get; set; } = Array.Empty<MarketAverage>();
public string[] EventsOverview { get; set; } = Array.Empty<string>();
} }
public class RewardUsersCommand public class RewardUsersCommand

View File

@ -1,10 +1,12 @@
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
using GethPlugin; using GethPlugin;
using Newtonsoft.Json;
namespace CodexContractsPlugin.Marketplace namespace CodexContractsPlugin.Marketplace
{ {
public partial class Request : RequestBase public partial class Request : RequestBase
{ {
[JsonIgnore]
public ulong BlockNumber { get; set; } public ulong BlockNumber { get; set; }
public byte[] RequestId { get; set; } public byte[] RequestId { get; set; }
@ -13,22 +15,26 @@ namespace CodexContractsPlugin.Marketplace
public partial class RequestFulfilledEventDTO public partial class RequestFulfilledEventDTO
{ {
[JsonIgnore]
public ulong BlockNumber { get; set; } public ulong BlockNumber { get; set; }
} }
public partial class RequestCancelledEventDTO public partial class RequestCancelledEventDTO
{ {
[JsonIgnore]
public ulong BlockNumber { get; set; } public ulong BlockNumber { get; set; }
} }
public partial class SlotFilledEventDTO public partial class SlotFilledEventDTO
{ {
[JsonIgnore]
public ulong BlockNumber { get; set; } public ulong BlockNumber { get; set; }
public EthAddress Host { get; set; } public EthAddress Host { get; set; }
} }
public partial class SlotFreedEventDTO public partial class SlotFreedEventDTO
{ {
[JsonIgnore]
public ulong BlockNumber { get; set; } public ulong BlockNumber { get; set; }
} }
} }

View File

@ -31,8 +31,14 @@ namespace BiblioTech.Commands
return; return;
} }
var eth = gethNode.GetEthBalance(addr); var eth = 0.Eth();
var testTokens = contracts.GetTestTokenBalance(addr); var testTokens = 0.TestTokens();
await Task.Run(() =>
{
eth = gethNode.GetEthBalance(addr);
testTokens = contracts.GetTestTokenBalance(addr);
});
await context.Followup($"{context.Command.User.Username} has {eth} and {testTokens}."); await context.Followup($"{context.Command.User.Username} has {eth} and {testTokens}.");
} }

View File

@ -33,8 +33,14 @@ namespace BiblioTech.Commands
var report = new List<string>(); var report = new List<string>();
var sentEth = ProcessEth(gethNode, addr, report); Transaction<Ether>? sentEth = null;
var mintedTokens = ProcessTokens(contracts, addr, report); Transaction<TestToken>? mintedTokens = null;
await Task.Run(() =>
{
sentEth = ProcessEth(gethNode, addr, report);
mintedTokens = ProcessTokens(contracts, addr, report);
});
Program.UserRepo.AddMintEventForUser(userId, addr, sentEth, mintedTokens); Program.UserRepo.AddMintEventForUser(userId, addr, sentEth, mintedTokens);

View File

@ -22,6 +22,9 @@ namespace BiblioTech
[Uniform("rewards-channel-name", "rc", "REWARDSCHANNELNAME", false, "Name of the Discord server channel where participation rewards will be announced.")] [Uniform("rewards-channel-name", "rc", "REWARDSCHANNELNAME", false, "Name of the Discord server channel where participation rewards will be announced.")]
public string RewardsChannelName { get; set; } = ""; public string RewardsChannelName { get; set; } = "";
[Uniform("chain-events-channel-name", "cc", "CHAINEVENTSCHANNELNAME", false, "Name of the Discord server channel where chain events will be posted.")]
public string ChainEventsChannelName { get; set; } = "";
[Uniform("reward-api-port", "rp", "REWARDAPIPORT", false, "TCP listen port for the reward API.")] [Uniform("reward-api-port", "rp", "REWARDAPIPORT", false, "TCP listen port for the reward API.")]
public int RewardApiPort { get; set; } = 31080; public int RewardApiPort { get; set; } = 31080;

View File

@ -9,16 +9,15 @@ namespace BiblioTech.Rewards
{ {
private readonly DiscordSocketClient client; private readonly DiscordSocketClient client;
private readonly SocketTextChannel? rewardsChannel; private readonly SocketTextChannel? rewardsChannel;
private readonly SocketTextChannel? eventsChannel;
private readonly RewardRepo repo = new RewardRepo(); private readonly RewardRepo repo = new RewardRepo();
public RoleDriver(DiscordSocketClient client) public RoleDriver(DiscordSocketClient client)
{ {
this.client = client; this.client = client;
if (!string.IsNullOrEmpty(Program.Config.RewardsChannelName)) rewardsChannel = GetChannel(Program.Config.RewardsChannelName);
{ eventsChannel = GetChannel(Program.Config.ChainEventsChannelName);
rewardsChannel = GetGuild().TextChannels.SingleOrDefault(c => c.Name == Program.Config.RewardsChannelName);
}
} }
public async Task GiveRewards(GiveRewardsCommand rewards) public async Task GiveRewards(GiveRewardsCommand rewards)
@ -34,6 +33,22 @@ namespace BiblioTech.Rewards
rewardsChannel); rewardsChannel);
await context.ProcessGiveRewardsCommand(LookUpUsers(rewards)); await context.ProcessGiveRewardsCommand(LookUpUsers(rewards));
await ProcessChainEvents(rewards.EventsOverview);
}
private SocketTextChannel? GetChannel(string name)
{
if (string.IsNullOrEmpty(name)) return null;
return GetGuild().TextChannels.SingleOrDefault(c => c.Name == name);
}
private async Task ProcessChainEvents(string[] eventsOverview)
{
if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return;
foreach (var e in eventsOverview)
{
await eventsChannel.SendMessageAsync(e);
}
} }
private async Task<Dictionary<ulong, IGuildUser>> LoadAllUsers(SocketGuild guild) private async Task<Dictionary<ulong, IGuildUser>> LoadAllUsers(SocketGuild guild)

View File

@ -1,5 +1,6 @@
using CodexContractsPlugin; using CodexContractsPlugin;
using CodexContractsPlugin.Marketplace; using CodexContractsPlugin.Marketplace;
using Newtonsoft.Json;
using Utils; using Utils;
namespace TestNetRewarder namespace TestNetRewarder
@ -11,11 +12,13 @@ namespace TestNetRewarder
public ChainState(HistoricState historicState, ICodexContracts contracts, BlockInterval blockRange) public ChainState(HistoricState historicState, ICodexContracts contracts, BlockInterval blockRange)
{ {
NewRequests = contracts.GetStorageRequests(blockRange); NewRequests = contracts.GetStorageRequests(blockRange);
historicState.CleanUpOldRequests();
historicState.ProcessNewRequests(NewRequests); historicState.ProcessNewRequests(NewRequests);
historicState.UpdateStorageRequests(contracts); historicState.UpdateStorageRequests(contracts);
StartedRequests = historicState.StorageRequests.Where(r => r.RecentlyStarted).ToArray(); StartedRequests = historicState.StorageRequests.Where(r => r.RecentlyStarted).ToArray();
FinishedRequests = historicState.StorageRequests.Where(r => r.RecentlyFinished).ToArray(); FinishedRequests = historicState.StorageRequests.Where(r => r.RecentlyFinished).ToArray();
ChangedRequests = historicState.StorageRequests.Where(r => r.RecentlyChanged).ToArray();
RequestFulfilledEvents = contracts.GetRequestFulfilledEvents(blockRange); RequestFulfilledEvents = contracts.GetRequestFulfilledEvents(blockRange);
RequestCancelledEvents = contracts.GetRequestCancelledEvents(blockRange); RequestCancelledEvents = contracts.GetRequestCancelledEvents(blockRange);
SlotFilledEvents = contracts.GetSlotFilledEvents(blockRange); SlotFilledEvents = contracts.GetSlotFilledEvents(blockRange);
@ -27,9 +30,78 @@ namespace TestNetRewarder
public StorageRequest[] AllRequests => historicState.StorageRequests; public StorageRequest[] AllRequests => historicState.StorageRequests;
public StorageRequest[] StartedRequests { get; private set; } public StorageRequest[] StartedRequests { get; private set; }
public StorageRequest[] FinishedRequests { get; private set; } public StorageRequest[] FinishedRequests { get; private set; }
public StorageRequest[] ChangedRequests { get; private set; }
public RequestFulfilledEventDTO[] RequestFulfilledEvents { get; } public RequestFulfilledEventDTO[] RequestFulfilledEvents { get; }
public RequestCancelledEventDTO[] RequestCancelledEvents { get; } public RequestCancelledEventDTO[] RequestCancelledEvents { get; }
public SlotFilledEventDTO[] SlotFilledEvents { get; } public SlotFilledEventDTO[] SlotFilledEvents { get; }
public SlotFreedEventDTO[] SlotFreedEvents { get; } public SlotFreedEventDTO[] SlotFreedEvents { get; }
public string[] GenerateOverview()
{
var entries = new List<StringBlockNumberPair>();
entries.AddRange(ChangedRequests.Select(ToPair));
entries.AddRange(RequestFulfilledEvents.Select(ToPair));
entries.AddRange(RequestCancelledEvents.Select(ToPair));
entries.AddRange(SlotFilledEvents.Select(ToPair));
entries.AddRange(SlotFreedEvents.Select(ToPair));
entries.Sort(new StringUtcComparer());
return entries.Select(ToLine).ToArray();
}
private StringBlockNumberPair ToPair(StorageRequest r)
{
return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.Request.BlockNumber);
}
private StringBlockNumberPair ToPair(RequestFulfilledEventDTO r)
{
return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber);
}
private StringBlockNumberPair ToPair(RequestCancelledEventDTO r)
{
return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber);
}
private StringBlockNumberPair ToPair(SlotFilledEventDTO r)
{
return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber);
}
private StringBlockNumberPair ToPair(SlotFreedEventDTO r)
{
return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber);
}
private string ToLine(StringBlockNumberPair pair)
{
return $"[{pair.Number}] {pair.Str}";
}
public class StringBlockNumberPair
{
public StringBlockNumberPair(string str, ulong number)
{
Str = str;
Number = number;
}
public string Str { get; }
public ulong Number { get; }
}
public class StringUtcComparer : IComparer<StringBlockNumberPair>
{
public int Compare(StringBlockNumberPair? x, StringBlockNumberPair? y)
{
if (x == null && y == null) return 0;
if (x == null) return 1;
if (y == null) return -1;
return x.Number.CompareTo(y.Number);
}
}
} }
} }

View File

@ -22,6 +22,9 @@ namespace TestNetRewarder
[Uniform("market-insights", "mi", "MARKETINSIGHTS", false, "Semi-colon separated integers. Each represents a multiple of intervals, for which a market insights average will be generated.")] [Uniform("market-insights", "mi", "MARKETINSIGHTS", false, "Semi-colon separated integers. Each represents a multiple of intervals, for which a market insights average will be generated.")]
public string MarketInsights { get; set; } = "1;96"; public string MarketInsights { get; set; } = "1;96";
[Uniform("events-overview", "eo", "EVENTSOVERVIEW", false, "When greater than zero, chain event summary will be generated. (default 1)")]
public int CreateChainEventsOverview { get; set; } = 1;
public string LogPath public string LogPath
{ {
get get

View File

@ -1,6 +1,7 @@
using CodexContractsPlugin; using CodexContractsPlugin;
using CodexContractsPlugin.Marketplace; using CodexContractsPlugin.Marketplace;
using GethPlugin; using GethPlugin;
using Newtonsoft.Json;
namespace TestNetRewarder namespace TestNetRewarder
{ {
@ -19,6 +20,17 @@ namespace TestNetRewarder
{ {
foreach (var r in storageRequests) r.Update(contracts); foreach (var r in storageRequests) r.Update(contracts);
} }
public void CleanUpOldRequests()
{
storageRequests.RemoveAll(r =>
r.State == RequestState.Cancelled ||
r.State == RequestState.Finished ||
r.State == RequestState.Failed
);
foreach (var r in storageRequests) r.IsNew = false;
}
} }
public class StorageRequest public class StorageRequest
@ -27,17 +39,26 @@ namespace TestNetRewarder
{ {
Request = request; Request = request;
Hosts = Array.Empty<EthAddress>(); Hosts = Array.Empty<EthAddress>();
IsNew = true;
} }
public Request Request { get; } public Request Request { get; }
public EthAddress[] Hosts { get; private set; } public EthAddress[] Hosts { get; private set; }
public RequestState State { get; private set; } public RequestState State { get; private set; }
public bool IsNew { get; set; }
[JsonIgnore]
public bool RecentlyStarted { get; private set; } public bool RecentlyStarted { get; private set; }
[JsonIgnore]
public bool RecentlyFinished { get; private set; } public bool RecentlyFinished { get; private set; }
[JsonIgnore]
public bool RecentlyChanged { get; private set; }
public void Update(ICodexContracts contracts) public void Update(ICodexContracts contracts)
{ {
Hosts = GetHosts(contracts); var newHosts = GetHosts(contracts);
var newState = contracts.GetRequestState(Request); var newState = contracts.GetRequestState(Request);
@ -49,7 +70,25 @@ namespace TestNetRewarder
State == RequestState.Started && State == RequestState.Started &&
newState == RequestState.Finished; newState == RequestState.Finished;
RecentlyChanged =
IsNew ||
State != newState ||
HostsChanged(newHosts);
State = newState; State = newState;
Hosts = newHosts;
}
private bool HostsChanged(EthAddress[] newHosts)
{
if (newHosts.Length != Hosts.Length) return true;
foreach (var newHost in newHosts)
{
if (!Hosts.Contains(newHost)) return true;
}
return false;
} }
private EthAddress[] GetHosts(ICodexContracts contracts) private EthAddress[] GetHosts(ICodexContracts contracts)

View File

@ -65,29 +65,36 @@ namespace TestNetRewarder
} }
var marketAverages = GetMarketAverages(chainState); var marketAverages = GetMarketAverages(chainState);
var eventsOverview = GenerateEventsOverview(chainState);
log.Log($"Found {outgoingRewards.Count} rewards to send. Found {marketAverages.Length} market averages."); log.Log($"Found {outgoingRewards.Count} rewards to send. Found {marketAverages.Length} market averages.");
if (outgoingRewards.Any()) if (outgoingRewards.Any())
{ {
if (!await SendRewardsCommand(outgoingRewards, marketAverages)) if (!await SendRewardsCommand(outgoingRewards, marketAverages, eventsOverview))
{ {
log.Error("Failed to send reward command."); log.Error("Failed to send reward command.");
} }
} }
} }
private string[] GenerateEventsOverview(ChainState chainState)
{
return chainState.GenerateOverview();
}
private MarketAverage[] GetMarketAverages(ChainState chainState) private MarketAverage[] GetMarketAverages(ChainState chainState)
{ {
return marketTracker.ProcessChainState(chainState); return marketTracker.ProcessChainState(chainState);
} }
private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards, MarketAverage[] marketAverages) private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards, MarketAverage[] marketAverages, string[] eventsOverview)
{ {
var cmd = new GiveRewardsCommand var cmd = new GiveRewardsCommand
{ {
Rewards = outgoingRewards.ToArray(), Rewards = outgoingRewards.ToArray(),
Averages = marketAverages.ToArray() Averages = marketAverages.ToArray(),
EventsOverview = eventsOverview
}; };
log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd)); log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd));