Merge branch 'better-chain-events'
This commit is contained in:
commit
933d5e7d4d
@ -1,4 +1,5 @@
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using GethPlugin;
|
||||
using Logging;
|
||||
using System.Numerics;
|
||||
using Utils;
|
||||
@ -11,7 +12,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||
void OnRequestFinished(IChainStateRequest request);
|
||||
void OnRequestFulfilled(IChainStateRequest request);
|
||||
void OnRequestCancelled(IChainStateRequest request);
|
||||
void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex);
|
||||
void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex);
|
||||
void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex);
|
||||
}
|
||||
|
||||
@ -116,7 +117,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||
if (r == null) return;
|
||||
r.Hosts.Add(request.Host, (int)request.SlotIndex);
|
||||
r.Log($"[{request.Block.BlockNumber}] SlotFilled (host:'{request.Host}', slotIndex:{request.SlotIndex})");
|
||||
handler.OnSlotFilled(r, request.SlotIndex);
|
||||
handler.OnSlotFilled(r, request.Host, request.SlotIndex);
|
||||
}
|
||||
|
||||
private void ApplyEvent(SlotFreedEventDTO request)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using GethPlugin;
|
||||
using System.Numerics;
|
||||
|
||||
namespace CodexContractsPlugin.ChainMonitor
|
||||
{
|
||||
@ -20,7 +21,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||
{
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
public void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,14 @@ namespace BiblioTech
|
||||
public class CommandHandler
|
||||
{
|
||||
private readonly DiscordSocketClient client;
|
||||
private readonly CustomReplacement replacement;
|
||||
private readonly BaseCommand[] commands;
|
||||
private readonly ILog log;
|
||||
|
||||
public CommandHandler(ILog log, DiscordSocketClient client, params BaseCommand[] commands)
|
||||
public CommandHandler(ILog log, DiscordSocketClient client, CustomReplacement replacement, params BaseCommand[] commands)
|
||||
{
|
||||
this.client = client;
|
||||
this.replacement = replacement;
|
||||
this.commands = commands;
|
||||
this.log = log;
|
||||
client.Ready += Client_Ready;
|
||||
@ -31,7 +33,7 @@ namespace BiblioTech
|
||||
var adminChannels = guild.TextChannels.Where(Program.AdminChecker.IsAdminChannel).ToArray();
|
||||
if (adminChannels == null || !adminChannels.Any()) throw new Exception("No admin message channel");
|
||||
Program.AdminChecker.SetAdminChannel(adminChannels.First());
|
||||
Program.RoleDriver = new RoleDriver(client, log);
|
||||
Program.RoleDriver = new RoleDriver(client, log, replacement);
|
||||
|
||||
var builders = commands.Select(c =>
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using BiblioTech.Options;
|
||||
using BiblioTech.Rewards;
|
||||
|
||||
namespace BiblioTech.Commands
|
||||
{
|
||||
@ -10,12 +11,14 @@ namespace BiblioTech.Commands
|
||||
private readonly AddSprCommand addSprCommand;
|
||||
private readonly ClearSprsCommand clearSprsCommand;
|
||||
private readonly GetSprCommand getSprCommand;
|
||||
private readonly LogReplaceCommand logReplaceCommand;
|
||||
|
||||
public AdminCommand(SprCommand sprCommand)
|
||||
public AdminCommand(SprCommand sprCommand, CustomReplacement replacement)
|
||||
{
|
||||
addSprCommand = new AddSprCommand(sprCommand);
|
||||
clearSprsCommand = new ClearSprsCommand(sprCommand);
|
||||
getSprCommand = new GetSprCommand(sprCommand);
|
||||
logReplaceCommand = new LogReplaceCommand(replacement);
|
||||
}
|
||||
|
||||
public override string Name => "admin";
|
||||
@ -29,7 +32,8 @@ namespace BiblioTech.Commands
|
||||
whoIsCommand,
|
||||
addSprCommand,
|
||||
clearSprsCommand,
|
||||
getSprCommand
|
||||
getSprCommand,
|
||||
logReplaceCommand
|
||||
};
|
||||
|
||||
protected override async Task Invoke(CommandContext context)
|
||||
@ -52,6 +56,7 @@ namespace BiblioTech.Commands
|
||||
await addSprCommand.CommandHandler(context);
|
||||
await clearSprsCommand.CommandHandler(context);
|
||||
await getSprCommand.CommandHandler(context);
|
||||
await logReplaceCommand.CommandHandler(context);
|
||||
}
|
||||
|
||||
public class ClearUserAssociationCommand : SubCommandOption
|
||||
@ -194,7 +199,7 @@ namespace BiblioTech.Commands
|
||||
}
|
||||
}
|
||||
|
||||
public class GetSprCommand: SubCommandOption
|
||||
public class GetSprCommand : SubCommandOption
|
||||
{
|
||||
private readonly SprCommand sprCommand;
|
||||
|
||||
@ -210,5 +215,56 @@ namespace BiblioTech.Commands
|
||||
await context.Followup("SPRs: " + string.Join(", ", sprCommand.Get().Select(s => $"'{s}'")));
|
||||
}
|
||||
}
|
||||
|
||||
public class LogReplaceCommand : SubCommandOption
|
||||
{
|
||||
private readonly CustomReplacement replacement;
|
||||
private readonly StringOption fromOption = new StringOption("from", "string to replace", true);
|
||||
private readonly StringOption toOption = new StringOption("to", "string to replace with", false);
|
||||
|
||||
public LogReplaceCommand(CustomReplacement replacement)
|
||||
: base(name: "logreplace",
|
||||
description: "Replaces all occurances of 'from' with 'to' in ChainEvent messages. Leave 'to' empty to remove a replacement.")
|
||||
{
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
public override CommandOption[] Options => new[] { fromOption, toOption };
|
||||
|
||||
protected override async Task onSubCommand(CommandContext context)
|
||||
{
|
||||
var from = await fromOption.Parse(context);
|
||||
var to = await toOption.Parse(context);
|
||||
|
||||
if (string.IsNullOrEmpty(from))
|
||||
{
|
||||
await context.Followup("'from' not received");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.Length < 5)
|
||||
{
|
||||
await context.Followup("'from' must be length 5 or greater.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(to))
|
||||
{
|
||||
replacement.Remove(from);
|
||||
await context.Followup($"Replace for '{from}' removed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (to.Length < 5)
|
||||
{
|
||||
await context.Followup("'to' must be length 5 or greater.");
|
||||
return;
|
||||
}
|
||||
|
||||
replacement.Add(from, to);
|
||||
await context.Followup($"Replace added '{from}' -->> '{to}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace BiblioTech
|
||||
public class Program
|
||||
{
|
||||
private DiscordSocketClient client = null!;
|
||||
private readonly CustomReplacement replacement = new CustomReplacement();
|
||||
|
||||
public static Configuration Config { get; private set; } = null!;
|
||||
public static UserRepo UserRepo { get; } = new UserRepo();
|
||||
@ -73,13 +74,13 @@ namespace BiblioTech
|
||||
var notifyCommand = new NotifyCommand();
|
||||
var associateCommand = new UserAssociateCommand(notifyCommand);
|
||||
var sprCommand = new SprCommand();
|
||||
var handler = new CommandHandler(Log, client,
|
||||
var handler = new CommandHandler(Log, client, replacement,
|
||||
new GetBalanceCommand(associateCommand),
|
||||
new MintCommand(associateCommand),
|
||||
sprCommand,
|
||||
associateCommand,
|
||||
notifyCommand,
|
||||
new AdminCommand(sprCommand),
|
||||
new AdminCommand(sprCommand, replacement),
|
||||
new MarketCommand()
|
||||
);
|
||||
|
||||
|
72
Tools/BiblioTech/Rewards/ChainEventsSender.cs
Normal file
72
Tools/BiblioTech/Rewards/ChainEventsSender.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using Discord.WebSocket;
|
||||
using Logging;
|
||||
|
||||
namespace BiblioTech.Rewards
|
||||
{
|
||||
public class ChainEventsSender
|
||||
{
|
||||
private readonly ILog log;
|
||||
private readonly CustomReplacement replacement;
|
||||
private readonly SocketTextChannel? eventsChannel;
|
||||
|
||||
public ChainEventsSender(ILog log, CustomReplacement replacement, SocketTextChannel? eventsChannel)
|
||||
{
|
||||
this.log = log;
|
||||
this.replacement = replacement;
|
||||
this.eventsChannel = eventsChannel;
|
||||
}
|
||||
|
||||
public async Task ProcessChainEvents(string[] eventsOverview)
|
||||
{
|
||||
if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return;
|
||||
try
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var users = Program.UserRepo.GetAllUserData();
|
||||
|
||||
foreach (var e in eventsOverview)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e))
|
||||
{
|
||||
var @event = ApplyReplacements(users, e);
|
||||
await eventsChannel.SendMessageAsync(@event);
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("Failed to process chain events: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string ApplyReplacements(UserData[] users, string msg)
|
||||
{
|
||||
var result = ApplyUserAddressReplacements(users, msg);
|
||||
result = ApplyCustomReplacements(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private string ApplyUserAddressReplacements(UserData[] users, string msg)
|
||||
{
|
||||
foreach (var user in users)
|
||||
{
|
||||
if (user.CurrentAddress != null &&
|
||||
!string.IsNullOrEmpty(user.CurrentAddress.Address) &&
|
||||
!string.IsNullOrEmpty(user.Name))
|
||||
{
|
||||
msg = msg.Replace(user.CurrentAddress.Address, user.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private string ApplyCustomReplacements(string result)
|
||||
{
|
||||
return replacement.Apply(result);
|
||||
}
|
||||
}
|
||||
}
|
34
Tools/BiblioTech/Rewards/CustomReplacement.cs
Normal file
34
Tools/BiblioTech/Rewards/CustomReplacement.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace BiblioTech.Rewards
|
||||
{
|
||||
public class CustomReplacement
|
||||
{
|
||||
private readonly Dictionary<string, string> replacements = new Dictionary<string, string>();
|
||||
|
||||
public void Add(string from, string to)
|
||||
{
|
||||
if (replacements.ContainsKey(from))
|
||||
{
|
||||
replacements[from] = to;
|
||||
}
|
||||
else
|
||||
{
|
||||
replacements.Add(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string from)
|
||||
{
|
||||
replacements.Remove(from);
|
||||
}
|
||||
|
||||
public string Apply(string msg)
|
||||
{
|
||||
var result = msg;
|
||||
foreach (var pair in replacements)
|
||||
{
|
||||
result.Replace(pair.Key, pair.Value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,15 +11,15 @@ namespace BiblioTech.Rewards
|
||||
private readonly DiscordSocketClient client;
|
||||
private readonly ILog log;
|
||||
private readonly SocketTextChannel? rewardsChannel;
|
||||
private readonly SocketTextChannel? eventsChannel;
|
||||
private readonly ChainEventsSender eventsSender;
|
||||
private readonly RewardRepo repo = new RewardRepo();
|
||||
|
||||
public RoleDriver(DiscordSocketClient client, ILog log)
|
||||
public RoleDriver(DiscordSocketClient client, ILog log, CustomReplacement replacement)
|
||||
{
|
||||
this.client = client;
|
||||
this.log = log;
|
||||
rewardsChannel = GetChannel(Program.Config.RewardsChannelId);
|
||||
eventsChannel = GetChannel(Program.Config.ChainEventsChannelId);
|
||||
eventsSender = new ChainEventsSender(log, replacement, GetChannel(Program.Config.ChainEventsChannelId));
|
||||
}
|
||||
|
||||
public async Task GiveRewards(GiveRewardsCommand rewards)
|
||||
@ -31,7 +31,7 @@ namespace BiblioTech.Rewards
|
||||
await ProcessRewards(rewards);
|
||||
}
|
||||
|
||||
await ProcessChainEvents(rewards.EventsOverview);
|
||||
await eventsSender.ProcessChainEvents(rewards.EventsOverview);
|
||||
}
|
||||
|
||||
private async Task ProcessRewards(GiveRewardsCommand rewards)
|
||||
@ -60,29 +60,6 @@ namespace BiblioTech.Rewards
|
||||
return GetGuild().TextChannels.SingleOrDefault(c => c.Id == id);
|
||||
}
|
||||
|
||||
private async Task ProcessChainEvents(string[] eventsOverview)
|
||||
{
|
||||
if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return;
|
||||
try
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
foreach (var e in eventsOverview)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e))
|
||||
{
|
||||
await eventsChannel.SendMessageAsync(e);
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("Failed to process chain events: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Dictionary<ulong, IGuildUser>> LoadAllUsers(SocketGuild guild)
|
||||
{
|
||||
log.Log("Loading all users..");
|
||||
|
66
Tools/BiblioTech/UserData.cs
Normal file
66
Tools/BiblioTech/UserData.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using CodexContractsPlugin;
|
||||
using GethPlugin;
|
||||
|
||||
namespace BiblioTech
|
||||
{
|
||||
public class UserData
|
||||
{
|
||||
public UserData(ulong discordId, string name, DateTime createdUtc, EthAddress? currentAddress, List<UserAssociateAddressEvent> associateEvents, List<UserMintEvent> mintEvents, bool notificationsEnabled)
|
||||
{
|
||||
DiscordId = discordId;
|
||||
Name = name;
|
||||
CreatedUtc = createdUtc;
|
||||
CurrentAddress = currentAddress;
|
||||
AssociateEvents = associateEvents;
|
||||
MintEvents = mintEvents;
|
||||
NotificationsEnabled = notificationsEnabled;
|
||||
}
|
||||
|
||||
public ulong DiscordId { get; }
|
||||
public string Name { get; }
|
||||
public DateTime CreatedUtc { get; }
|
||||
public EthAddress? CurrentAddress { get; set; }
|
||||
public List<UserAssociateAddressEvent> AssociateEvents { get; }
|
||||
public List<UserMintEvent> MintEvents { get; }
|
||||
public bool NotificationsEnabled { get; set; }
|
||||
|
||||
public string[] CreateOverview()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
$"name: '{Name}' - id:{DiscordId}",
|
||||
$"joined: {CreatedUtc.ToString("o")}",
|
||||
$"current address: {CurrentAddress}",
|
||||
$"{AssociateEvents.Count + MintEvents.Count} total bot events."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class UserAssociateAddressEvent
|
||||
{
|
||||
public UserAssociateAddressEvent(DateTime utc, EthAddress? newAddress)
|
||||
{
|
||||
Utc = utc;
|
||||
NewAddress = newAddress;
|
||||
}
|
||||
|
||||
public DateTime Utc { get; }
|
||||
public EthAddress? NewAddress { get; }
|
||||
}
|
||||
|
||||
public class UserMintEvent
|
||||
{
|
||||
public UserMintEvent(DateTime utc, EthAddress usedAddress, Transaction<Ether>? ethReceived, Transaction<TestToken>? testTokensMinted)
|
||||
{
|
||||
Utc = utc;
|
||||
UsedAddress = usedAddress;
|
||||
EthReceived = ethReceived;
|
||||
TestTokensMinted = testTokensMinted;
|
||||
}
|
||||
|
||||
public DateTime Utc { get; }
|
||||
public EthAddress UsedAddress { get; }
|
||||
public Transaction<Ether>? EthReceived { get; }
|
||||
public Transaction<TestToken>? TestTokensMinted { get; }
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ namespace BiblioTech
|
||||
public class UserRepo
|
||||
{
|
||||
private readonly object repoLock = new object();
|
||||
private readonly Dictionary<ulong, UserData> cache = new Dictionary<ulong, UserData>();
|
||||
|
||||
public bool AssociateUserWithAddress(IUser user, EthAddress address)
|
||||
{
|
||||
@ -33,6 +34,12 @@ namespace BiblioTech
|
||||
}
|
||||
}
|
||||
|
||||
public UserData[] GetAllUserData()
|
||||
{
|
||||
if (cache.Count == 0) LoadAllUserData();
|
||||
return cache.Values.ToArray();
|
||||
}
|
||||
|
||||
public void AddMintEventForUser(IUser user, EthAddress usedAddress, Transaction<Ether>? eth, Transaction<TestToken>? tokens)
|
||||
{
|
||||
lock (repoLock)
|
||||
@ -151,12 +158,19 @@ namespace BiblioTech
|
||||
|
||||
private UserData? GetUserData(IUser user)
|
||||
{
|
||||
if (cache.ContainsKey(user.Id))
|
||||
{
|
||||
return cache[user.Id];
|
||||
}
|
||||
|
||||
var filename = GetFilename(user);
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonConvert.DeserializeObject<UserData>(File.ReadAllText(filename))!;
|
||||
var userData = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(filename))!;
|
||||
cache.Add(userData.DiscordId, userData);
|
||||
return userData;
|
||||
}
|
||||
|
||||
private UserData GetOrCreate(IUser user)
|
||||
@ -181,6 +195,15 @@ namespace BiblioTech
|
||||
var filename = GetFilename(userData);
|
||||
if (File.Exists(filename)) File.Delete(filename);
|
||||
File.WriteAllText(filename, JsonConvert.SerializeObject(userData));
|
||||
|
||||
if (cache.ContainsKey(userData.DiscordId))
|
||||
{
|
||||
cache[userData.DiscordId] = userData;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache.Add(userData.DiscordId, userData);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetFilename(IUser user)
|
||||
@ -197,66 +220,29 @@ namespace BiblioTech
|
||||
{
|
||||
return Path.Combine(Program.Config.UserDataPath, discordId.ToString() + ".json");
|
||||
}
|
||||
}
|
||||
|
||||
public class UserData
|
||||
{
|
||||
public UserData(ulong discordId, string name, DateTime createdUtc, EthAddress? currentAddress, List<UserAssociateAddressEvent> associateEvents, List<UserMintEvent> mintEvents, bool notificationsEnabled)
|
||||
private void LoadAllUserData()
|
||||
{
|
||||
DiscordId = discordId;
|
||||
Name = name;
|
||||
CreatedUtc = createdUtc;
|
||||
CurrentAddress = currentAddress;
|
||||
AssociateEvents = associateEvents;
|
||||
MintEvents = mintEvents;
|
||||
NotificationsEnabled = notificationsEnabled;
|
||||
}
|
||||
|
||||
public ulong DiscordId { get; }
|
||||
public string Name { get; }
|
||||
public DateTime CreatedUtc { get; }
|
||||
public EthAddress? CurrentAddress { get; set; }
|
||||
public List<UserAssociateAddressEvent> AssociateEvents { get; }
|
||||
public List<UserMintEvent> MintEvents { get; }
|
||||
public bool NotificationsEnabled { get; set; }
|
||||
|
||||
public string[] CreateOverview()
|
||||
{
|
||||
return new[]
|
||||
try
|
||||
{
|
||||
$"name: '{Name}' - id:{DiscordId}",
|
||||
$"joined: {CreatedUtc.ToString("o")}",
|
||||
$"current address: {CurrentAddress}",
|
||||
$"{AssociateEvents.Count + MintEvents.Count} total bot events."
|
||||
};
|
||||
var files = Directory.GetFiles(Program.Config.UserDataPath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userData = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(file))!;
|
||||
if (userData != null && userData.DiscordId > 0)
|
||||
{
|
||||
cache.Add(userData.DiscordId, userData);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log.Error("Exception while trying to load all user data: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UserAssociateAddressEvent
|
||||
{
|
||||
public UserAssociateAddressEvent(DateTime utc, EthAddress? newAddress)
|
||||
{
|
||||
Utc = utc;
|
||||
NewAddress = newAddress;
|
||||
}
|
||||
|
||||
public DateTime Utc { get; }
|
||||
public EthAddress? NewAddress { get; }
|
||||
}
|
||||
|
||||
public class UserMintEvent
|
||||
{
|
||||
public UserMintEvent(DateTime utc, EthAddress usedAddress, Transaction<Ether>? ethReceived, Transaction<TestToken>? testTokensMinted)
|
||||
{
|
||||
Utc = utc;
|
||||
UsedAddress = usedAddress;
|
||||
EthReceived = ethReceived;
|
||||
TestTokensMinted = testTokensMinted;
|
||||
}
|
||||
|
||||
public DateTime Utc { get; }
|
||||
public EthAddress UsedAddress { get; }
|
||||
public Transaction<Ether>? EthReceived { get; }
|
||||
public Transaction<TestToken>? TestTokensMinted { get; }
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
using Logging;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class BufferLogger : ILog
|
||||
{
|
||||
private readonly List<string> lines = new List<string>();
|
||||
|
||||
public void AddStringReplace(string from, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public LogFile CreateSubfile(string ext = "log")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Debug(string message = "", int skipFrames = 0)
|
||||
{
|
||||
lines.Add(message);
|
||||
}
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
lines.Add($"Error: {message}");
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
lines.Add(message);
|
||||
}
|
||||
|
||||
public string[] Get()
|
||||
{
|
||||
var result = lines.ToArray();
|
||||
lines.Clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using GethPlugin;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
@ -32,9 +33,9 @@ namespace TestNetRewarder
|
||||
foreach (var handler in handlers) handler.OnRequestFulfilled(request);
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
public void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnSlotFilled(request, slotIndex);
|
||||
foreach (var handler in handlers) handler.OnSlotFilled(request, host, slotIndex);
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
|
122
Tools/TestNetRewarder/EventsFormatter.cs
Normal file
122
Tools/TestNetRewarder/EventsFormatter.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using GethPlugin;
|
||||
using System.Numerics;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class EventsFormatter : IChainStateChangeHandler
|
||||
{
|
||||
private static readonly string nl = Environment.NewLine;
|
||||
private readonly List<string> events = new List<string>();
|
||||
|
||||
public string[] GetEvents()
|
||||
{
|
||||
var result = events.ToArray();
|
||||
events.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AddError(string error)
|
||||
{
|
||||
AddBlock("📢 **Error**", error);
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
AddRequestBlock(request, "New Request",
|
||||
$"Client: {request.Client}",
|
||||
$"Content: {request.Request.Content.Cid}",
|
||||
$"Duration: {BigIntToDuration(request.Request.Ask.Duration)}",
|
||||
$"Expiry: {BigIntToDuration(request.Request.Expiry)}",
|
||||
$"Collateral: {BitIntToTestTokens(request.Request.Ask.Collateral)}",
|
||||
$"Reward: {BitIntToTestTokens(request.Request.Ask.Reward)}",
|
||||
$"Number of Slots: {request.Request.Ask.Slots}",
|
||||
$"Slot Tolerance: {request.Request.Ask.MaxSlotLoss}",
|
||||
$"Slot Size: {BigIntToByteSize(request.Request.Ask.SlotSize)}"
|
||||
);
|
||||
}
|
||||
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
AddRequestBlock(request, "Cancelled");
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
AddRequestBlock(request, "Finished");
|
||||
}
|
||||
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
AddRequestBlock(request, "Started");
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex)
|
||||
{
|
||||
AddRequestBlock(request, "Slot Filled",
|
||||
$"Host: {host}",
|
||||
$"Slot Index: {slotIndex}"
|
||||
);
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
AddRequestBlock(request, "Slot Freed",
|
||||
$"Slot Index: {slotIndex}"
|
||||
);
|
||||
}
|
||||
|
||||
private void AddRequestBlock(IChainStateRequest request, string eventName, params string[] content)
|
||||
{
|
||||
var blockNumber = $"[{request.Request.Block.BlockNumber}]";
|
||||
var title = $"{blockNumber} **{eventName}** `{request.Request.Id}`";
|
||||
AddBlock(title, content);
|
||||
}
|
||||
|
||||
private void AddBlock(string title, params string[] content)
|
||||
{
|
||||
events.Add(FormatBlock(title, content));
|
||||
}
|
||||
|
||||
private string FormatBlock(string title, params string[] content)
|
||||
{
|
||||
if (content == null || !content.Any())
|
||||
{
|
||||
return $"{title}{nl}{nl}";
|
||||
}
|
||||
|
||||
return string.Join(nl,
|
||||
new string[]
|
||||
{
|
||||
title,
|
||||
"```"
|
||||
}
|
||||
.Concat(content)
|
||||
.Concat(new string[]
|
||||
{
|
||||
"```"
|
||||
})
|
||||
) + nl + nl;
|
||||
}
|
||||
|
||||
private string BigIntToDuration(BigInteger big)
|
||||
{
|
||||
var span = TimeSpan.FromSeconds((int)big);
|
||||
return Time.FormatDuration(span);
|
||||
}
|
||||
|
||||
private string BigIntToByteSize(BigInteger big)
|
||||
{
|
||||
var size = new ByteSize((long)big);
|
||||
return size.ToString();
|
||||
}
|
||||
|
||||
private string BitIntToTestTokens(BigInteger big)
|
||||
{
|
||||
var tt = new TestToken(big);
|
||||
return tt.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using Logging;
|
||||
using System.Numerics;
|
||||
|
||||
@ -48,7 +49,7 @@ namespace TestNetRewarder
|
||||
{
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
public void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace TestNetRewarder
|
||||
private readonly RequestBuilder builder;
|
||||
private readonly RewardChecker rewardChecker;
|
||||
private readonly MarketTracker marketTracker;
|
||||
private readonly BufferLogger bufferLogger;
|
||||
private readonly EventsFormatter eventsFormatter;
|
||||
private readonly ChainState chainState;
|
||||
private readonly BotClient client;
|
||||
private readonly ILog log;
|
||||
@ -23,14 +23,15 @@ namespace TestNetRewarder
|
||||
builder = new RequestBuilder();
|
||||
rewardChecker = new RewardChecker(builder);
|
||||
marketTracker = new MarketTracker(config, log);
|
||||
bufferLogger = new BufferLogger();
|
||||
eventsFormatter = new EventsFormatter();
|
||||
|
||||
var handler = new ChainChangeMux(
|
||||
rewardChecker.Handler,
|
||||
marketTracker
|
||||
marketTracker,
|
||||
eventsFormatter
|
||||
);
|
||||
|
||||
chainState = new ChainState(new LogSplitter(log, bufferLogger), contracts, handler, config.HistoryStartUtc);
|
||||
chainState = new ChainState(log, contracts, handler, config.HistoryStartUtc);
|
||||
}
|
||||
|
||||
public async Task OnNewSegment(TimeRange timeRange)
|
||||
@ -40,9 +41,9 @@ namespace TestNetRewarder
|
||||
chainState.Update(timeRange.To);
|
||||
|
||||
var averages = marketTracker.GetAverages();
|
||||
var lines = RemoveFirstLine(bufferLogger.Get());
|
||||
var events = eventsFormatter.GetEvents();
|
||||
|
||||
var request = builder.Build(averages, lines);
|
||||
var request = builder.Build(averages, events);
|
||||
if (request.HasAny())
|
||||
{
|
||||
await client.SendRewards(request);
|
||||
@ -52,16 +53,9 @@ namespace TestNetRewarder
|
||||
{
|
||||
var msg = "Exception processing time segment: " + ex;
|
||||
log.Error(msg);
|
||||
bufferLogger.Error(msg);
|
||||
eventsFormatter.AddError(msg);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string[] RemoveFirstLine(string[] lines)
|
||||
{
|
||||
//if (!lines.Any()) return Array.Empty<string>();
|
||||
//return lines.Skip(1).ToArray();
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,11 +53,10 @@ namespace TestNetRewarder
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
public void OnSlotFilled(IChainStateRequest request, EthAddress host, BigInteger slotIndex)
|
||||
{
|
||||
if (MeetsRequirements(CheckType.HostFilledSlot, request))
|
||||
{
|
||||
var host = request.Hosts.GetHost((int)slotIndex);
|
||||
if (host != null)
|
||||
{
|
||||
GiveReward(reward, host);
|
||||
|
Loading…
x
Reference in New Issue
Block a user