Skip to content

Commit

Permalink
Merge remote-tracking branch 'TCLrainbow/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingsman0530 committed Feb 21, 2024
2 parents 8d890f4 + ec2e597 commit 02aaaf5
Show file tree
Hide file tree
Showing 19 changed files with 691 additions and 327 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ name: C# bot

on:
push:
branches: [ "main", "debug-workflow" ]
# branches: [ "main", "debug-workflow" ]
paths: ["**.cs", ".github/workflows/bot.yml"]
pull_request:
branches: [ "main" ]
# branches: [ "main" ]
paths: ["**.cs", ".github/workflows/bot.yml"]

defaults:
Expand All @@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Build
Expand All @@ -39,7 +39,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
Expand Down
21 changes: 18 additions & 3 deletions Bot/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,25 @@ dotnet_style_prefer_collection_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_diagnostic.CA1852.severity = suggestion

# C# files
[*.cs]
# ReSharper
[*.cs]
resharper_function_never_returns_highlighting = none
resharper_arrange_object_creation_when_type_not_evident_highlighting = none
resharper_invert_if_highlighting = none
resharper_arrange_redundant_parentheses_highlighting = none
[Discord/Cmds.cs]
resharper_unused_type_global_highlighting = none
resharper_unused_member_global_highlighting = none
[Secrets.cs]
resharper_not_accessed_field_global_highlighting = none
[Sql/*.cs]
resharper_auto_property_can_be_made_get_only_global_highlighting = none
resharper_member_can_be_private_global_highlighting = none

# C# files
[*.cs]

# New line preferences
csharp_new_line_before_open_brace = all
Expand Down Expand Up @@ -61,7 +75,7 @@ dotnet_style_qualification_for_event = false:suggestion

# Types: use keywords instead of BCL types, and permit var only when the type is clear
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:none
csharp_style_var_when_type_is_apparent = false:suggestion
csharp_style_var_elsewhere = false:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
Expand Down Expand Up @@ -177,6 +191,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
dotnet_diagnostic.IDE0058.severity = none
dotnet_diagnostic.IDE0010.severity = none
csharp_style_deconstructed_variable_declaration = true:suggestion

# C++ Files
[*.{cpp,h,in}]
Expand Down
6 changes: 3 additions & 3 deletions Bot/Bot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@

<ItemGroup>
<PackageReference Include="ClashOfClans" Version="9.0.0" />
<PackageReference Include="Discord.Net.Interactions" Version="3.13.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.13.0" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.7" />
<PackageReference Include="Discord.Net.Interactions" Version="3.13.1" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.13.1" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.8" />
</ItemGroup>

<ItemGroup>
Expand Down
73 changes: 73 additions & 0 deletions Bot/Clash/ClanUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using ClashOfClans.Models;

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers.CSharpAddAccessibilityModifiersDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0040" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.AddRequiredParentheses.CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0048" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.AddRequiredParentheses.CSharpAddRequiredPatternParenthesesDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0048" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.Analyzers.ForEachCast.CSharpForEachCastDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0220" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.Analyzers.MakeFieldReadonly.CSharpMakeFieldReadonlyDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0044" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.Analyzers.MatchFolderAndNamespace.CSharpMatchFolderAndNamespaceDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0130" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.Analyzers.RemoveUnnecessaryNullableDirective.CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0240" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.Analyzers.UseCoalesceExpression.CSharpUseCoalesceExpressionForIfNullStatementCheckDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0270" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertToBlockScopedNamespaceDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0160" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs

Check warning on line 1 in Bot/Clash/ClanUtil.cs

View workflow job for this annotation

GitHub Actions / inspect

"[AD0001] Analyzer 'Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertToFileScopedNamespaceDiagnosticAnalyzer' threw an exception of type 'System.MissingMethodException' with message 'Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'.'. System.MissingMethodException: Method not found: 'Microsoft.CodeAnalysis.DiagnosticSeverity Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.get_MinimumReportedSeverity()'. at Microsoft.CodeAnalysis.CodeStyle.AbstractBuiltInCodeStyleDiagnosticAnalyzer.Initialize(AnalysisContext context) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteInitializeMethod>b__37_0(ValueTuple`2 data) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 200 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in Z:\BuildAgent\work\3b7ce003563d6f8f\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1177 ----- Suppress the following diagnostics to disable this analyzer: IDE0161" on /home/runner/work/coc-clan/coc-clan/Bot/Clash/ClanUtil.cs
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 ClanUtil()
{
_clan = new();
_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)
{
ClanMember? clanMember = clan.MemberList!.FirstOrDefault(m => m.Tag == dbMember);
if (clanMember == null)
{
ClanMember m = new()
{
Tag = dbMember
};
c._members[dbMember] = m; // Fake a member
}
else
{
c._members[dbMember] = clanMember;
clan.MemberList!.Remove(clanMember);
}
}
return c;
}

internal static ClanUtil FromPoll(Clan clan)
{
ClanUtil c = new(clan, new(Coc.Clan._members));
foreach (ClanMember member in clan.MemberList!)
{
c._members[member.Tag] = member;
if (Coc.Clan.HasMember(member))
{
c._existingMembers[member.Tag] = member;
c._leavingMembers.Remove(member.Tag);
}
else
{
c._joiningMembers[member.Tag] = member;
}
}
return c;
}

// ReSharper disable All
private bool HasMember(ClanMember member) => _members.ContainsKey(member.Tag);
// ReSharper enable All
}

186 changes: 186 additions & 0 deletions Bot/Clash/Coc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using ClashOfClans;
using ClashOfClans.Core;
using ClashOfClans.Models;
using Hyperstellar.Discord;
using Hyperstellar.Sql;

namespace Hyperstellar.Clash;

internal static class Coc
{
private const string ClanId = "#2QU2UCJJC"; // 2G8LP8PVV
private static readonly ClashOfClansClient s_client = new(Secrets.s_coc);
private static bool s_inMaintenance;
internal static ClanUtil Clan { get; private set; } = new();
internal static event Action<ClanMember>? s_eventMemberJoined;
internal static event Action<ClanMember, string?>? s_eventMemberLeft;
internal static event Func<Dictionary<string, DonationTuple>, Task>? EventDonated;
internal static event Func<Dictionary<string, DonationTuple>, Task>? EventDonatedFold;

static Coc() => Dc.EventBotReady += BotReadyAsync;

private static async Task BotReadyAsync()
{
while (true)
{
try
{
await PollAsync();
s_inMaintenance = false;
await Task.Delay(10000);
}
catch (ClashOfClansException ex)
{
if (ex.Error.Reason == "inMaintenance")
{
if (!s_inMaintenance)
{
s_inMaintenance = true;
await Dc.SendLogAsync(ex.Error.Message);
}
await Task.Delay(60000);
}
else
{
await Dc.ExceptionAsync(ex);
}
}
catch (Exception ex)
{
await Dc.ExceptionAsync(ex);
}
}
}

private static void CheckMembersJoined(ClanUtil clan)
{
if (clan._joiningMembers.Count == 0)
{
return;
}

string[] members = [.. clan._joiningMembers.Keys];
Db.AddMembers(members);
string membersMsg = string.Join(", ", members);
Console.WriteLine($"{membersMsg} joined");

foreach (ClanMember m in clan._joiningMembers.Values)
{
s_eventMemberJoined!(m);
}
}

private static void CheckMembersLeft(ClanUtil clan)
{
if (clan._leavingMembers.Count == 0)
{
return;
}

foreach ((string id, ClanMember member) in clan._leavingMembers)
{
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();
}
s_eventMemberLeft!(member, altId); // This is before Db.DelMem below so that we can remap Donation to new mainId
}

string[] members = [.. clan._leavingMembers.Keys];
Db.DeleteMembers(members);
string membersMsg = string.Join(", ", members);
Console.WriteLine($"{membersMsg} left");
}

private static async Task<Clan> GetClanAsync() => await s_client.Clans.GetClanAsync(ClanId);

private static async Task PollAsync()
{
Clan clan = await GetClanAsync();

if (clan.MemberList == null)
{
return;
}

ClanUtil clanUtil = ClanUtil.FromPoll(clan);
CheckMembersJoined(clanUtil);
CheckMembersLeft(clanUtil);
await Task.WhenAll([
CheckDonationsAsync(clanUtil)
]);
Clan = clanUtil;
}

private static async Task CheckDonationsAsync(ClanUtil clan)
{
Dictionary<string, DonationTuple> donDelta = [];
foreach (string tag in clan._existingMembers.Keys)
{
ClanMember current = clan._members[tag];
ClanMember previous = Clan._members[tag];
if (current.Donations > previous.Donations || current.DonationsReceived > previous.DonationsReceived)
{
donDelta[current.Tag] = new(current.Donations - previous.Donations, current.DonationsReceived - previous.DonationsReceived);
}
}

foreach (KeyValuePair<string, DonationTuple> dd in donDelta)
{
Console.WriteLine($"{dd.Key}: {dd.Value._donated} {dd.Value._received}");
}

// Fold alt data into main
Dictionary<string, DonationTuple> foldedDelta = [];
foreach (KeyValuePair<string, DonationTuple> delta in donDelta)
{
string tag = delta.Key;
DonationTuple dt = delta.Value;
Alt? alt = new Member(tag).TryToAlt();
if (alt != null)
{
tag = alt.MainId;
}
foldedDelta[tag] = foldedDelta.TryGetValue(tag, out DonationTuple value) ? value.Add(dt) : dt;
}

if (foldedDelta.Count > 0)
{
Console.WriteLine("---");
}

foreach (KeyValuePair<string, DonationTuple> dd in foldedDelta)
{
Console.WriteLine($"{dd.Key}: {dd.Value._donated} {dd.Value._received}");
}

ICollection<Task> tasks = [];
if (donDelta.Count > 0)
{
tasks.Add(EventDonated!(donDelta));
}
if (foldedDelta.Count > 0)
{
tasks.Add(EventDonatedFold!(foldedDelta));
}
await Task.WhenAll(tasks);
}

internal static string? GetMemberId(string name)
{
ClanMember? result = Clan._clan.MemberList!.FirstOrDefault(m => m.Name == name);
return result?.Tag;
}

internal static ClanMember GetMember(string id) => Clan._members[id];

internal static async Task InitAsync() => Clan = ClanUtil.FromInit(await GetClanAsync());
}
Loading

0 comments on commit 02aaaf5

Please sign in to comment.