Merge branch 'feature/bot-upgrade'
This commit is contained in:
commit
605004286a
|
@ -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
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in New Issue