2023-10-22 07:32:03 +00:00
|
|
|
|
using CodexContractsPlugin;
|
2023-10-25 09:25:27 +00:00
|
|
|
|
using Discord;
|
2023-10-22 07:32:03 +00:00
|
|
|
|
using GethPlugin;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
|
|
|
|
namespace BiblioTech
|
|
|
|
|
{
|
|
|
|
|
public class UserRepo
|
|
|
|
|
{
|
|
|
|
|
private readonly object repoLock = new object();
|
2024-06-27 08:44:37 +00:00
|
|
|
|
private readonly Dictionary<ulong, UserData> cache = new Dictionary<ulong, UserData>();
|
2023-10-22 07:32:03 +00:00
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
public bool AssociateUserWithAddress(IUser user, EthAddress address)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return SetUserAddress(user, address);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
public void ClearUserAssociatedAddress(IUser user)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
SetUserAddress(user, null);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 09:27:07 +00:00
|
|
|
|
public void SetUserNotificationPreference(IUser user, bool enableNotifications)
|
|
|
|
|
{
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
|
|
|
|
SetUserNotification(user, enableNotifications);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 08:44:37 +00:00
|
|
|
|
public UserData[] GetAllUserData()
|
|
|
|
|
{
|
|
|
|
|
if (cache.Count == 0) LoadAllUserData();
|
|
|
|
|
return cache.Values.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-15 10:09:15 +00:00
|
|
|
|
public void AddMintEventForUser(IUser user, EthAddress usedAddress, Transaction<Ether>? eth, Transaction<TestToken>? tokens)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var userData = GetOrCreate(user);
|
|
|
|
|
userData.MintEvents.Add(new UserMintEvent(DateTime.UtcNow, usedAddress, eth, tokens));
|
|
|
|
|
SaveUserData(userData);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
public EthAddress? GetCurrentAddressForUser(IUser user)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return GetOrCreate(user).CurrentAddress;
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
public string[] GetInteractionReport(IUser user)
|
2023-10-22 08:10:52 +00:00
|
|
|
|
{
|
2023-11-02 14:04:53 +00:00
|
|
|
|
var result = new List<string>
|
|
|
|
|
{
|
|
|
|
|
$"User report create on {DateTime.UtcNow.ToString("o")}"
|
|
|
|
|
};
|
2023-10-22 08:10:52 +00:00
|
|
|
|
|
|
|
|
|
lock (repoLock)
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var userData = GetUserData(user);
|
|
|
|
|
if (userData == null)
|
2023-10-22 08:10:52 +00:00
|
|
|
|
{
|
|
|
|
|
result.Add("User has not joined the test net.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
result.Add("User joined on " + userData.CreatedUtc.ToString("o"));
|
|
|
|
|
result.Add("Current address: " + userData.CurrentAddress);
|
|
|
|
|
foreach (var ae in userData.AssociateEvents)
|
2023-10-22 08:10:52 +00:00
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
result.Add($"{ae.Utc.ToString("o")} - Address set to: {ae.NewAddress}");
|
2023-10-22 08:10:52 +00:00
|
|
|
|
}
|
2023-10-25 09:25:27 +00:00
|
|
|
|
foreach (var me in userData.MintEvents)
|
2023-10-22 08:10:52 +00:00
|
|
|
|
{
|
2023-12-15 10:09:15 +00:00
|
|
|
|
if (me.EthReceived != null)
|
|
|
|
|
{
|
|
|
|
|
result.Add($"{me.Utc.ToString("o")} - Sent {me.EthReceived.TokenAmount} to {me.UsedAddress}. ({me.EthReceived.TransactionHash})");
|
|
|
|
|
}
|
|
|
|
|
if (me.TestTokensMinted != null)
|
|
|
|
|
{
|
|
|
|
|
result.Add($"{me.Utc.ToString("o")} - Minted {me.TestTokensMinted.TokenAmount} to {me.UsedAddress}. ({me.TestTokensMinted.TransactionHash})");
|
|
|
|
|
}
|
2023-10-22 08:10:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 09:13:34 +00:00
|
|
|
|
public string[] GetUserReport(IUser user)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var userData = GetUserData(user);
|
2023-11-09 09:13:34 +00:00
|
|
|
|
if (userData == null) return new[] { "User has not joined the test net." };
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return userData.CreateOverview();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 09:13:34 +00:00
|
|
|
|
public string[] GetUserReport(EthAddress ethAddress)
|
2023-10-25 09:25:27 +00:00
|
|
|
|
{
|
|
|
|
|
var userData = GetUserDataForAddress(ethAddress);
|
2023-11-09 09:13:34 +00:00
|
|
|
|
if (userData == null) return new[] { "No user is using this eth address." };
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return userData.CreateOverview();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-20 14:56:03 +00:00
|
|
|
|
public UserData? GetUserDataForAddress(EthAddress? address)
|
|
|
|
|
{
|
|
|
|
|
if (address == null) return null;
|
|
|
|
|
|
|
|
|
|
// If this becomes a performance problem, switch to in-memory cached list.
|
|
|
|
|
var files = Directory.GetFiles(Program.Config.UserDataPath);
|
|
|
|
|
foreach (var file in files)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var user = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(file))!;
|
|
|
|
|
if (user.CurrentAddress != null &&
|
|
|
|
|
user.CurrentAddress.Address == address.Address)
|
|
|
|
|
{
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
private bool SetUserAddress(IUser user, EthAddress? address)
|
|
|
|
|
{
|
|
|
|
|
if (GetUserDataForAddress(address) != null)
|
2023-10-25 08:25:00 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var userData = GetOrCreate(user);
|
|
|
|
|
userData.CurrentAddress = address;
|
|
|
|
|
userData.AssociateEvents.Add(new UserAssociateAddressEvent(DateTime.UtcNow, address));
|
|
|
|
|
SaveUserData(userData);
|
2023-10-25 08:25:00 +00:00
|
|
|
|
return true;
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 09:27:07 +00:00
|
|
|
|
private void SetUserNotification(IUser user, bool notifyEnabled)
|
|
|
|
|
{
|
|
|
|
|
var userData = GetUserData(user);
|
|
|
|
|
if (userData == null) return;
|
|
|
|
|
userData.NotificationsEnabled = notifyEnabled;
|
|
|
|
|
SaveUserData(userData);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
private UserData? GetUserData(IUser user)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
2024-06-27 08:44:37 +00:00
|
|
|
|
if (cache.ContainsKey(user.Id))
|
|
|
|
|
{
|
|
|
|
|
return cache[user.Id];
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var filename = GetFilename(user);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
if (!File.Exists(filename))
|
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2024-06-27 08:44:37 +00:00
|
|
|
|
var userData = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(filename))!;
|
|
|
|
|
cache.Add(userData.DiscordId, userData);
|
|
|
|
|
return userData;
|
2023-10-25 09:25:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private UserData GetOrCreate(IUser user)
|
|
|
|
|
{
|
|
|
|
|
var userData = GetUserData(user);
|
|
|
|
|
if (userData == null)
|
|
|
|
|
{
|
|
|
|
|
return CreateAndSaveNewUserData(user);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
2023-10-25 09:25:27 +00:00
|
|
|
|
return userData;
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
private UserData CreateAndSaveNewUserData(IUser user)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
2023-12-20 14:56:03 +00:00
|
|
|
|
var newUser = new UserData(user.Id, user.GlobalName, DateTime.UtcNow, null, new List<UserAssociateAddressEvent>(), new List<UserMintEvent>(), true);
|
2023-10-25 09:25:27 +00:00
|
|
|
|
SaveUserData(newUser);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
return newUser;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 09:25:27 +00:00
|
|
|
|
private void SaveUserData(UserData userData)
|
2023-10-22 07:32:03 +00:00
|
|
|
|
{
|
2023-10-25 09:25:27 +00:00
|
|
|
|
var filename = GetFilename(userData);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
if (File.Exists(filename)) File.Delete(filename);
|
2023-10-25 09:25:27 +00:00
|
|
|
|
File.WriteAllText(filename, JsonConvert.SerializeObject(userData));
|
2024-06-27 08:44:37 +00:00
|
|
|
|
|
|
|
|
|
if (cache.ContainsKey(userData.DiscordId))
|
|
|
|
|
{
|
|
|
|
|
cache[userData.DiscordId] = userData;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cache.Add(userData.DiscordId, userData);
|
|
|
|
|
}
|
2023-10-25 09:25:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetFilename(IUser user)
|
|
|
|
|
{
|
|
|
|
|
return GetFilename(user.Id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetFilename(UserData userData)
|
|
|
|
|
{
|
|
|
|
|
return GetFilename(userData.DiscordId);
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetFilename(ulong discordId)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(Program.Config.UserDataPath, discordId.ToString() + ".json");
|
|
|
|
|
}
|
2023-10-25 09:25:27 +00:00
|
|
|
|
|
2024-06-27 08:44:37 +00:00
|
|
|
|
private void LoadAllUserData()
|
2023-10-25 09:25:27 +00:00
|
|
|
|
{
|
2024-06-27 08:44:37 +00:00
|
|
|
|
try
|
2023-11-09 09:13:34 +00:00
|
|
|
|
{
|
2024-06-27 08:44:37 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2023-10-22 07:32:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|