/ Tools / BiblioTech / UserRepo.cs
UserRepo.cs
  1  using CodexContractsPlugin;
  2  using Discord;
  3  using GethPlugin;
  4  using Newtonsoft.Json;
  5  using Utils;
  6  
  7  namespace BiblioTech
  8  {
  9      public class UserRepo
 10      {
 11          private readonly object repoLock = new object();
 12          private readonly Dictionary<ulong, UserData> cache = new Dictionary<ulong, UserData>();
 13  
 14          public SetAddressResponse AssociateUserWithAddress(IUser user, EthAddress address)
 15          {
 16              lock (repoLock)
 17              {
 18                  return SetUserAddress(user, address);
 19              }
 20          }
 21  
 22          public void ClearUserAssociatedAddress(IUser user)
 23          {
 24              lock (repoLock)
 25              {
 26                  SetUserAddress(user, null);
 27              }
 28          }
 29  
 30          public void SetUserNotificationPreference(IUser user, bool enableNotifications)
 31          {
 32              lock (repoLock)
 33              {
 34                  SetUserNotification(user, enableNotifications);
 35              }
 36          }
 37  
 38          public UserData[] GetAllUserData()
 39          {
 40              if (cache.Count == 0) LoadAllUserData();
 41              return cache.Values.ToArray();  
 42          }
 43  
 44          public UserData GetUser(IUser user)
 45          {
 46              if (cache.Count == 0) LoadAllUserData();
 47              return GetOrCreate(user);
 48          }
 49  
 50          public void AddMintEventForUser(IUser user, EthAddress usedAddress, Transaction<Ether>? eth, Transaction<TestToken>? tokens)
 51          {
 52              lock (repoLock)
 53              {
 54                  var userData = GetOrCreate(user);
 55                  userData.MintEvents.Add(new UserMintEvent(DateTime.UtcNow, usedAddress, eth, tokens));
 56                  SaveUserData(userData);
 57              }
 58          }
 59  
 60          public EthAddress? GetCurrentAddressForUser(IUser user)
 61          {
 62              lock (repoLock)
 63              {
 64                  return GetOrCreate(user).CurrentAddress;
 65              }
 66          }
 67  
 68          public string[] GetInteractionReport(IUser user)
 69          {
 70              var result = new List<string>
 71              {
 72                  $"User report create on {DateTime.UtcNow.ToString("o")}"
 73              };
 74  
 75              lock (repoLock)
 76              {
 77                  var userData = GetUserDataMaybe(user);
 78                  if (userData == null)
 79                  {
 80                      result.Add("User has not interacted with bot.");
 81                  }
 82                  else
 83                  {
 84                      result.Add("User joined on " + userData.CreatedUtc.ToString("o"));
 85                      result.Add("Current address: " + userData.CurrentAddress);
 86                      foreach (var ae in userData.AssociateEvents)
 87                      {
 88                          result.Add($"{ae.Utc.ToString("o")} - Address set to: {ae.NewAddress}");
 89                      }
 90                      foreach (var me in userData.MintEvents)
 91                      {
 92                          if (me.EthReceived != null)
 93                          {
 94                              result.Add($"{me.Utc.ToString("o")} - {me.EthReceived.ToString()} to {me.UsedAddress}. ({me.EthReceived.TransactionHash})");
 95                          }
 96                          if (me.TestTokensMinted != null)
 97                          {
 98                              result.Add($"{me.Utc.ToString("o")} - {me.TestTokensMinted.ToString()} to {me.UsedAddress}. ({me.TestTokensMinted.TransactionHash})");
 99                          }
100                      }
101                  }
102              }
103  
104              return result.ToArray();
105          }
106  
107          public string[] GetUserReport(IUser user)
108          {
109              var userData = GetUserDataMaybe(user);
110              if (userData == null) return new[] { "User has not joined the test net." };
111              return userData.CreateOverview();
112          }
113  
114          public string[] GetUserReport(EthAddress ethAddress)
115          {
116              var userData = GetUserDataForAddressMaybe(ethAddress);
117              if (userData == null) return new[] { "No user is using this eth address." };
118              return userData.CreateOverview();
119          }
120  
121          public UserData? GetUserDataForAddressMaybe(EthAddress? address)
122          {
123              if (address == null) return null;
124  
125              var lower = address.Address.ToLowerInvariant();
126              if (string.IsNullOrEmpty(lower)) return null;
127  
128              if (cache.Count == 0) LoadAllUserData();
129              foreach (var item in cache.Values)
130              {
131                  if (item.CurrentAddress != null &&
132                      item.CurrentAddress.Address.ToLowerInvariant() == lower)
133                  {
134                      return item;
135                  }
136              }
137  
138              return null;
139          }
140  
141          private SetAddressResponse SetUserAddress(IUser user, EthAddress? address)
142          {
143              if (GetUserDataForAddressMaybe(address) != null)
144              {
145                  return SetAddressResponse.AddressAlreadyInUse;
146              }
147  
148              var userData = GetOrCreate(user);
149              if (userData == null) return SetAddressResponse.CreateUserFailed;
150              userData.CurrentAddress = address;
151              userData.AssociateEvents.Add(new UserAssociateAddressEvent(DateTime.UtcNow, address));
152              SaveUserData(userData);
153              return SetAddressResponse.OK;
154          }
155  
156          private void SetUserNotification(IUser user, bool notifyEnabled)
157          {
158              var userData = GetOrCreate(user);
159              userData.NotificationsEnabled = notifyEnabled;
160              SaveUserData(userData);
161          }
162  
163          private UserData? GetUserDataMaybe(IUser user)
164          {
165              if (cache.ContainsKey(user.Id))
166              {
167                  return cache[user.Id];
168              }
169  
170              var filename = GetFilename(user);
171              if (!File.Exists(filename))
172              {
173                  return null;
174              }
175              var userData = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(filename))!;
176              cache.Add(userData.DiscordId, userData);
177              return userData;
178          }
179  
180          private UserData GetOrCreate(IUser user)
181          {
182              var userData = GetUserDataMaybe(user);
183              if (userData == null)
184              {
185                  return CreateAndSaveNewUserData(user);
186              }
187              return userData;
188          }
189  
190          private UserData CreateAndSaveNewUserData(IUser user)
191          {
192              var newUser = new UserData(user.Id, user.GlobalName, DateTime.UtcNow, null, new List<UserAssociateAddressEvent>(), new List<UserMintEvent>(), true);
193              SaveUserData(newUser);
194              return newUser;
195          }
196  
197          private void SaveUserData(UserData userData)
198          {
199              var filename = GetFilename(userData);
200              if (File.Exists(filename)) File.Delete(filename);
201              File.WriteAllText(filename, JsonConvert.SerializeObject(userData));
202  
203              if (cache.ContainsKey(userData.DiscordId))
204              {
205                  cache[userData.DiscordId] = userData;
206              }
207              else
208              {
209                  cache.Add(userData.DiscordId, userData);
210              }
211          }
212  
213          private static string GetFilename(IUser user)
214          {
215              return GetFilename(user.Id);
216          }
217  
218          private static string GetFilename(UserData userData)
219          {
220              return GetFilename(userData.DiscordId);
221          }
222  
223          private static string GetFilename(ulong discordId)
224          {
225              return Path.Combine(Program.Config.UserDataPath, discordId.ToString() + ".json");
226          }
227  
228          private void LoadAllUserData()
229          {
230              try
231              {
232                  var files = Directory.GetFiles(Program.Config.UserDataPath);
233                  foreach (var file in files)
234                  {
235                      try
236                      {
237                          var userData = JsonConvert.DeserializeObject<UserData>(File.ReadAllText(file))!;
238                          if (userData != null && userData.DiscordId > 0)
239                          {
240                              cache.Add(userData.DiscordId, userData);
241                          }
242                      }
243                      catch { }
244                  }
245              }
246              catch (Exception ex)
247              {
248                  Program.Log.Error("Exception while trying to load all user data: " + ex);
249              }
250          }
251      }
252  
253      public enum SetAddressResponse
254      {
255          OK,
256          AddressAlreadyInUse,
257          CreateUserFailed
258      }
259  }