-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Bot] Always save valid donation count
- Loading branch information
1 parent
bf64e3c
commit dfa4d67
Showing
11 changed files
with
447 additions
and
448 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using ClashOfClans.Models; | ||
using Hyperstellar.Sql; | ||
|
||
namespace Hyperstellar.Clash; | ||
|
||
internal sealed class ClanUtil | ||
{ | ||
internal readonly Clan _clan; | ||
internal readonly Dictionary<string, ClanMember> _members = []; | ||
internal readonly Dictionary<string, ClanMember> _existingMembers = []; | ||
internal readonly Dictionary<string, ClanMember> _joiningMembers = []; | ||
internal readonly Dictionary<string, ClanMember> _leavingMembers; | ||
|
||
private ClanUtil(Clan clan, Dictionary<string, ClanMember> leavingMembers) | ||
{ | ||
_clan = clan; | ||
_leavingMembers = leavingMembers; | ||
} | ||
|
||
internal static ClanUtil FromInit(Clan clan) | ||
{ | ||
ClanUtil c = new(clan, []); | ||
IEnumerable<string> existingMembers = Db.GetMembers().Select(m => m.CocId); | ||
foreach (string dbMember in existingMembers) | ||
{ | ||
bool stillExists = false; | ||
foreach (ClanMember clanMember in clan.MemberList!) | ||
Check notice on line 27 in Bot/Clash/ClanUtil.cs GitHub Actions / inspect
|
||
{ | ||
if (clanMember.Tag.Equals(dbMember)) | ||
{ | ||
c._members[dbMember] = clanMember; | ||
clan.MemberList.Remove(clanMember); | ||
stillExists = true; | ||
break; | ||
} | ||
} | ||
if (!stillExists) | ||
{ | ||
c._members[dbMember] = new(); // Fake a member | ||
} | ||
} | ||
return c; | ||
} | ||
|
||
internal static ClanUtil FromPoll(Clan clan) | ||
{ | ||
ClanUtil c = new(clan, new(Coc.s_clan._members)); | ||
foreach (ClanMember member in clan.MemberList!) | ||
{ | ||
c._members[member.Tag] = member; | ||
if (Coc.s_clan.HasMember(member)) | ||
{ | ||
c._existingMembers[member.Tag] = member; | ||
c._leavingMembers.Remove(member.Tag); | ||
} | ||
else | ||
{ | ||
c._joiningMembers[member.Tag] = member; | ||
} | ||
} | ||
return c; | ||
} | ||
|
||
internal bool HasMember(ClanMember member) => _members.ContainsKey(member.Tag); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
using ClashOfClans; | ||
using ClashOfClans.Models; | ||
using Hyperstellar.Sql; | ||
using static Hyperstellar.Discord.Dc; | ||
|
||
namespace Hyperstellar.Clash; | ||
|
||
internal static class Coc | ||
{ | ||
|
||
internal readonly struct DonationTuple(int donated, int received) | ||
{ | ||
internal readonly int _donated = donated; | ||
internal readonly int _received = received; | ||
|
||
internal DonationTuple Add(DonationTuple dt) => new(_donated + dt._donated, _received + dt._received); | ||
} | ||
|
||
private const string ClanId = "#2QU2UCJJC"; // 2G8LP8PVV | ||
private static readonly ClashOfClansClient s_client = new(Secrets.s_coc); | ||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. | ||
internal static ClanUtil s_clan { get; private set; } | ||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. | ||
|
||
private static void CheckMembersJoined(ClanUtil clan) | ||
{ | ||
if (clan._joiningMembers.Count == 0) | ||
{ | ||
return; | ||
} | ||
|
||
string[] members = [.. clan._joiningMembers.Keys]; | ||
bool isSuccess = Db.AddMembers(members); | ||
string membersMsg = string.Join(", ", members); | ||
if (isSuccess) | ||
{ | ||
Console.WriteLine($"{membersMsg} joined"); | ||
} | ||
else | ||
{ | ||
Console.Error.WriteLine($"ERROR MembersJoined {membersMsg}"); | ||
} | ||
|
||
foreach (string id in clan._joiningMembers.Keys) | ||
{ | ||
Donate25.MemberAdded(id); | ||
} | ||
} | ||
|
||
private static void CheckMembersLeft(ClanUtil clan) | ||
{ | ||
if (clan._leavingMembers.Count == 0) | ||
{ | ||
return; | ||
} | ||
|
||
foreach (string id in clan._leavingMembers.Keys) | ||
{ | ||
IEnumerable<Alt> alts = new Member(id).GetAltsByMain(); | ||
string? altId = null; | ||
if (alts.Any()) | ||
{ | ||
Alt alt = alts.First(); | ||
altId = alt.AltId; | ||
for (int i = 1; i < alts.Count(); i++) | ||
{ | ||
alts.ElementAt(i).UpdateMain(alt.AltId); | ||
} | ||
alt.Delete(); | ||
} | ||
Donate25.MemberRemoved(id, altId); | ||
} | ||
|
||
|
||
string[] members = [.. clan._leavingMembers.Keys]; | ||
bool isSuccess = Db.DeleteMembers(members); | ||
string membersMsg = string.Join(", ", members); | ||
if (isSuccess) | ||
{ | ||
Console.WriteLine($"{membersMsg} left"); | ||
} | ||
else | ||
{ | ||
Console.Error.WriteLine($"ERROR MembersLeft {membersMsg}"); | ||
} | ||
} | ||
|
||
private static async Task<Clan> GetClanAsync() => await s_client.Clans.GetClanAsync(ClanId); | ||
|
||
private static async Task PollAsync() | ||
{ | ||
Clan clan = await GetClanAsync(); | ||
if (clan == null) | ||
Check warning on line 93 in Bot/Clash/Coc.cs GitHub Actions / inspect
|
||
{ | ||
return; | ||
} | ||
|
||
if (clan.MemberList == null) | ||
{ | ||
return; | ||
} | ||
|
||
ClanUtil clanUtil = ClanUtil.FromPoll(clan); | ||
CheckMembersJoined(clanUtil); | ||
CheckMembersLeft(clanUtil); | ||
await Task.WhenAll([ | ||
CheckDonationsAsync(clanUtil), | ||
]); | ||
s_clan = clanUtil; | ||
} | ||
|
||
private static async Task CheckDonationsAsync(ClanUtil clan) | ||
{ | ||
Dictionary<string, DonationTuple> donationsDelta = []; | ||
Dictionary<string, DonationTuple> donationsTagDelta = []; | ||
foreach (string tag in clan._existingMembers.Keys) | ||
{ | ||
ClanMember current = clan._members[tag]; | ||
ClanMember previous = s_clan._members[tag]; | ||
if (current.Donations > previous.Donations || current.DonationsReceived > previous.DonationsReceived) | ||
{ | ||
donationsDelta[current.Name] = new(current.Donations - previous.Donations, current.DonationsReceived - previous.DonationsReceived); | ||
donationsTagDelta[current.Tag] = new(current.Donations - previous.Donations, current.DonationsReceived - previous.DonationsReceived); | ||
} | ||
|
||
} | ||
|
||
|
||
foreach (KeyValuePair<string, DonationTuple> dd in donationsTagDelta) | ||
{ | ||
Console.WriteLine($"{dd.Key}: {dd.Value._donated} {dd.Value._received}"); | ||
} | ||
|
||
// Fold alt data into main | ||
Dictionary<string, DonationTuple> folded = []; | ||
foreach (KeyValuePair<string, DonationTuple> donDelta in donationsTagDelta) | ||
{ | ||
string tag = donDelta.Key; | ||
DonationTuple dt = donDelta.Value; | ||
Member member = new(tag); | ||
Alt? alt = member.TryToAlt(); | ||
if (alt != null) | ||
{ | ||
tag = alt.MainId; | ||
} | ||
folded[tag] = folded.TryGetValue(tag, out DonationTuple value) ? value.Add(dt) : dt; | ||
} | ||
donationsTagDelta = folded; | ||
|
||
if (donationsTagDelta.Count > 0) | ||
{ | ||
Console.WriteLine("---"); | ||
} | ||
|
||
foreach (KeyValuePair<string, DonationTuple> dd in donationsTagDelta) | ||
{ | ||
Console.WriteLine($"{dd.Key}: {dd.Value._donated} {dd.Value._received}"); | ||
} | ||
|
||
// Everyone is main now | ||
foreach (KeyValuePair<string, DonationTuple> donDelta in donationsTagDelta) | ||
{ | ||
string tag = donDelta.Key; | ||
DonationTuple dt = donDelta.Value; | ||
int donated = dt._donated; | ||
int received = dt._received; | ||
|
||
if (donated > received) | ||
{ | ||
donated -= received; | ||
Donation donation = Db.GetDonation(tag)!; | ||
donation.Donated += (uint)donated; | ||
Console.WriteLine($"[Donate25] {tag} {donated}"); | ||
Db.UpdateDonation(donation); | ||
} | ||
} | ||
|
||
if (donationsDelta.Count > 0) | ||
{ | ||
await DonationsChangedAsync(donationsDelta); | ||
} | ||
} | ||
|
||
internal static string? GetMemberId(string name) | ||
{ | ||
ClanMember? result = s_clan._clan.MemberList!.FirstOrDefault(m => m.Name == name); | ||
return result?.Tag; | ||
} | ||
|
||
internal static ClanMember GetMember(string id) => s_clan._members[id]; | ||
|
||
internal static async Task InitAsync() => s_clan = ClanUtil.FromInit(await GetClanAsync()); | ||
|
||
internal static async Task BotReadyAsync() | ||
{ | ||
Donate25.Init(); | ||
_ = Task.Run(Donate25.CheckAsync); | ||
while (true) | ||
{ | ||
await PollAsync(); | ||
await Task.Delay(20000); | ||
} | ||
} | ||
} |
Oops, something went wrong.