268 lines
11 KiB
C#
268 lines
11 KiB
C#
// Project Makoto
|
|
// Copyright (C) 2024 Fortunevale
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY
|
|
|
|
using ProjectMakoto.Entities.Members;
|
|
|
|
namespace ProjectMakoto.Util.Initializers;
|
|
internal static class SyncTasks
|
|
{
|
|
internal static async Task GuildDownloadCompleted(Bot bot, DiscordClient sender, GuildDownloadCompletedEventArgs e)
|
|
{
|
|
if (bot.status.DiscordGuildDownloadCompleted)
|
|
return;
|
|
|
|
bot.status.DiscordGuildDownloadCompleted = true;
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
Log.Information("I'm on {GuildsCount} guilds.", e.Guilds.Count);
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
foreach (var user in bot.Users)
|
|
{
|
|
var userCache = new Dictionary<ulong, DiscordUser?>();
|
|
|
|
if (user.Value.LegacyBlockedUsers.Length > 0)
|
|
{
|
|
for (var i = 0; i < user.Value.LegacyBlockedUsers.Length; i++)
|
|
{
|
|
var b = user.Value.LegacyBlockedUsers[i];
|
|
|
|
if (!userCache.TryGetValue(b, out var victim))
|
|
{
|
|
if (bot.DiscordClient.GetFirstShard().TryGetUser(b, out var fetched))
|
|
userCache.Add(b, fetched);
|
|
else
|
|
userCache.Add(b, null);
|
|
|
|
victim = userCache[b];
|
|
}
|
|
|
|
if (victim is null || victim.Id == bot.DiscordClient.CurrentUser.Id || victim.Id == user.Key || victim.IsBot || (victim.Flags?.HasFlag(UserFlags.Staff) ?? false))
|
|
{
|
|
Log.Debug("Removing '{victim}' from '{owner}' blocklist", b, user.Value.Id);
|
|
i--;
|
|
user.Value.LegacyBlockedUsers = user.Value.LegacyBlockedUsers.Remove(x => x.ToString(), b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}).Add(bot);
|
|
|
|
for (var i = 0; i < 501; i++)
|
|
{
|
|
_ = bot.ExperienceHandler.CalculateLevelRequirement(i);
|
|
}
|
|
|
|
foreach (var guild in e.Guilds)
|
|
{
|
|
if (!bot.Guilds.ContainsKey(guild.Key))
|
|
bot.Guilds.Add(guild.Key, new Guild(bot, guild.Key));
|
|
|
|
if (bot.Guilds[guild.Key].BumpReminder.ChannelId != 0)
|
|
{
|
|
bot.BumpReminder.ScheduleBump(sender, guild.Key);
|
|
}
|
|
|
|
if (bot.Guilds[guild.Key].Crosspost.CrosspostChannels.Length != 0)
|
|
{
|
|
_ = Task.Run(async () =>
|
|
{
|
|
for (var i = 0; i < bot.Guilds[guild.Key].Crosspost.CrosspostChannels.Length; i++)
|
|
{
|
|
if (guild.Value is null)
|
|
return;
|
|
|
|
var ChannelId = bot.Guilds[guild.Key].Crosspost.CrosspostChannels[i];
|
|
|
|
Log.Debug("Checking channel '{ChannelId}' for missing crossposts..", ChannelId);
|
|
|
|
if (!guild.Value.Channels.ContainsKey(ChannelId))
|
|
return;
|
|
|
|
var Messages = await guild.Value.GetChannel(ChannelId).GetMessagesAsync(20);
|
|
|
|
if (Messages.Any(x => x.Flags.HasValue && !x.Flags.Value.HasMessageFlag(MessageFlags.Crossposted)))
|
|
foreach (var msg in Messages.Where(x => x.Flags.HasValue && !x.Flags.Value.HasMessageFlag(MessageFlags.Crossposted)))
|
|
{
|
|
Log.Debug("Handling missing crosspost message '{msg}' in '{ChannelId}' for '{guild}'..", msg.Id, msg.ChannelId, guild.Key);
|
|
|
|
var WaitTime = bot.Guilds[guild.Value.Id].Crosspost.DelayBeforePosting - msg.Id.GetSnowflakeTime().GetTotalSecondsSince();
|
|
|
|
if (WaitTime > 0)
|
|
await Task.Delay(TimeSpan.FromSeconds(WaitTime));
|
|
|
|
if (bot.Guilds[guild.Value.Id].Crosspost.DelayBeforePosting > 3)
|
|
_ = msg.DeleteReactionsEmojiAsync(DiscordEmoji.FromUnicode("🕒"));
|
|
|
|
await bot.Guilds[guild.Key].Crosspost.CrosspostWithRatelimit(sender, msg);
|
|
}
|
|
}
|
|
}).Add(bot);
|
|
}
|
|
}
|
|
|
|
_ = BasePlugin.RaisePreSyncTasksExecution(bot, e.Guilds.Values.Where(x => x != null));
|
|
|
|
try
|
|
{
|
|
await ExecuteSyncTasks(bot, bot.DiscordClient);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failed to run sync tasks");
|
|
}
|
|
|
|
_ = BasePlugin.RaisePostSyncTasksExecution(bot, e.Guilds.Values.Where(x => x != null));
|
|
}).Add(bot);
|
|
}
|
|
|
|
internal static DateTime lastSyncTaskTime = DateTime.MinValue;
|
|
internal static async Task ExecuteSyncTasks(Bot bot, DiscordShardedClient shardedClient)
|
|
{
|
|
if (lastSyncTaskTime.GetTimespanSince() < TimeSpan.FromSeconds(30))
|
|
return;
|
|
|
|
lastSyncTaskTime = DateTime.UtcNow;
|
|
|
|
var Guilds = shardedClient.GetGuilds();
|
|
|
|
ObservableList<Task> runningTasks = new();
|
|
|
|
void runningTasksUpdated(object sender, ObservableListUpdate<Task> e)
|
|
{
|
|
if (e is not null && e.NewItems is not null)
|
|
foreach (var b in e.NewItems)
|
|
{
|
|
_ = b.Add(bot);
|
|
}
|
|
}
|
|
|
|
runningTasks.ItemsChanged += runningTasksUpdated;
|
|
|
|
var startupTasksSuccess = 0;
|
|
|
|
foreach (var guild in Guilds)
|
|
{
|
|
while (runningTasks.Count >= 4 && !runningTasks.Any(x => x.IsCompleted))
|
|
await Task.Delay(100);
|
|
|
|
foreach (var task in runningTasks.ToList())
|
|
if (task.IsCompleted)
|
|
_ = runningTasks.Remove(task);
|
|
|
|
runningTasks.Add(Task.Run(async () =>
|
|
{
|
|
Log.Debug("Performing sync tasks for '{guild}'..", guild.Key);
|
|
|
|
if (bot.objectedUsers.Contains(guild.Value.OwnerId.Value) || bot.bannedUsers.ContainsKey(guild.Value.OwnerId.Value) || bot.bannedGuilds.ContainsKey(guild.Key))
|
|
{
|
|
Log.Information("Leaving guild '{guild}'..", guild.Key);
|
|
await guild.Value.LeaveAsync();
|
|
return;
|
|
}
|
|
|
|
var guildMembers = await guild.Value.GetAllMembersAsync();
|
|
var guildBans = await guild.Value.GetBansAsync();
|
|
|
|
foreach (var member in guildMembers)
|
|
{
|
|
bot.ExperienceHandler.CheckExperience(member.Id, guild.Value);
|
|
|
|
if (bot.Guilds[guild.Key].Members[member.Id].FirstJoinDate == DateTime.MinValue)
|
|
bot.Guilds[guild.Key].Members[member.Id].FirstJoinDate = member.JoinedAt.UtcDateTime;
|
|
|
|
if (bot.Guilds[guild.Key].Members[member.Id].LastLeaveDate != DateTime.MinValue)
|
|
bot.Guilds[guild.Key].Members[member.Id].LastLeaveDate = DateTime.MinValue;
|
|
|
|
bot.Guilds[guild.Key].Members[member.Id].MemberRoles = member.Roles.Select(x => new MemberRole()
|
|
{
|
|
Id = x.Id,
|
|
Name = x.Name,
|
|
}).ToArray();
|
|
|
|
bot.Guilds[guild.Key].Members[member.Id].SavedNickname = member.Nickname;
|
|
|
|
await bot.Guilds[guild.Key].Members[member.Id].PerformAutoKickChecks(guild.Value, member);
|
|
}
|
|
|
|
foreach (var databaseMember in bot.Guilds[guild.Key].Members)
|
|
{
|
|
if (!guildMembers.Any(x => x.Id == databaseMember.Key))
|
|
{
|
|
if (bot.Guilds[guild.Key].Members[databaseMember.Key].LastLeaveDate == DateTime.MinValue)
|
|
bot.Guilds[guild.Key].Members[databaseMember.Key].LastLeaveDate = DateTime.UtcNow;
|
|
}
|
|
}
|
|
|
|
foreach (var banEntry in guildBans)
|
|
{
|
|
if (!bot.Guilds[guild.Key].Members.ContainsKey(banEntry.User.Id))
|
|
continue;
|
|
|
|
if (bot.Guilds[guild.Key].Members[banEntry.User.Id].MemberRoles.Length > 0)
|
|
bot.Guilds[guild.Key].Members[banEntry.User.Id].MemberRoles = Array.Empty<MemberRole>();
|
|
|
|
if (bot.Guilds[guild.Key].Members[banEntry.User.Id].SavedNickname != "")
|
|
bot.Guilds[guild.Key].Members[banEntry.User.Id].SavedNickname = "";
|
|
}
|
|
|
|
if (bot.Guilds[guild.Key].InviteTracker.Enabled)
|
|
{
|
|
await InviteTrackerEvents.UpdateCachedInvites(bot, guild.Value);
|
|
}
|
|
|
|
startupTasksSuccess++;
|
|
}));
|
|
}
|
|
|
|
foreach (var guild in Guilds)
|
|
{
|
|
try
|
|
{
|
|
List<DiscordThreadChannel> Threads = new();
|
|
|
|
while (true)
|
|
{
|
|
var t = await guild.Value.GetActiveThreadsAsync();
|
|
|
|
foreach (var b in t.ReturnedThreads.Values)
|
|
{
|
|
if (!Threads.Contains(b) && b is not null)
|
|
Threads.Add(b);
|
|
}
|
|
|
|
if (!t.HasMore)
|
|
break;
|
|
|
|
Log.Debug("Requesting more threads for '{guild}'", guild.Key);
|
|
}
|
|
|
|
foreach (var b in Threads.Where(x => x.CurrentMember is null))
|
|
{
|
|
Log.Debug("Joining thread on '{guild}': {thread}", guild.Key, b.Id);
|
|
b.JoinWithQueue(bot.ThreadJoinClient);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Failed to join threads on '{guild}'", guild.Key);
|
|
}
|
|
}
|
|
|
|
while (runningTasks.Any(x => !x.IsCompleted))
|
|
await Task.Delay(100);
|
|
|
|
runningTasks.ItemsChanged -= runningTasksUpdated;
|
|
runningTasks.Clear();
|
|
|
|
Log.Information("Sync Tasks successfully finished for {startupTasksSuccess}/{GuildCount} guilds.", startupTasksSuccess, Guilds.Count);
|
|
}
|
|
}
|