ProjectMakoto/ProjectMakoto/Util/ExperienceHandler.cs

240 lines
10 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
namespace ProjectMakoto.Util;
internal sealed class ExperienceHandler : RequiresTranslation
{
internal ExperienceHandler(Bot _bot) : base(_bot)
{
}
Translations.events.experience tKey
=> this.Bot.LoadedTranslations.Events.Experience;
private Dictionary<long, long> LevelCache = new();
internal int CalculateMessageExperience(DiscordMessage message)
{
if (message.Content.Length > 0)
{
if (Regex.IsMatch(message.Content, @"^(-|>>|;;|\$|\!|\!d|owo |/)"))
return 0;
}
var Points = 1;
if (message.ReferencedMessage is not null)
Points += 2;
if (message.Attachments is not null && string.IsNullOrWhiteSpace(message.Content))
Points -= 1;
if (RegexTemplates.Url.IsMatch(message.Content))
{
var ModifiedString = RegexTemplates.Url.Replace(message.Content, "");
if (ModifiedString.Length > 10)
Points += 1;
if (ModifiedString.Length > 25)
Points += 1;
if (ModifiedString.Length > 50)
Points += 1;
if (ModifiedString.Length > 75)
Points += 1;
}
else
{
if (message.Content.Length > 10)
Points += 1;
if (message.Content.Length > 25)
Points += 1;
if (message.Content.Length > 50)
Points += 1;
if (message.Content.Length > 75)
Points += 1;
}
return Points;
}
internal async Task ModifyExperience(ulong user, DiscordGuild guild, DiscordChannel channel, int Amount) => await this.ModifyExperience(await guild.GetMemberAsync(user), guild, channel, Amount);
internal async Task ModifyExperience(DiscordUser user, DiscordGuild guild, DiscordChannel channel, int Amount) => await this.ModifyExperience(await user.ConvertToMember(guild), guild, channel, Amount);
internal async Task ModifyExperience(DiscordMember user, DiscordGuild guild, DiscordChannel channel, int Amount)
{
if (user.IsBot)
return;
if (!this.Bot.Guilds[guild.Id].Experience.UseExperience)
return;
if (this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Points is > (long.MaxValue - 10000) or < (long.MinValue + 10000))
{
Log.Warning("Member '{User}' on '{Guild}' is within 10000 points of the experience limit. Resetting.", user.Id, guild.Id);
this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Points = 1;
}
this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Points += Amount;
var PreviousLevel = this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level;
this.CheckExperience(user.Id, guild);
if (this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level != PreviousLevel && channel != null && channel.Type is ChannelType.Text or ChannelType.PublicThread or ChannelType.PrivateThread)
{
DiscordEmbedBuilder embed = null;
if (this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level > PreviousLevel)
{
var build = this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level - PreviousLevel is 1
? $":stars: {this.tKey.GainedLevel.Get(this.Bot.Guilds[guild.Id]).Build(new TVar("User", user.Mention), new TVar("Count", 1))}\n" +
$"{this.tKey.NewLevel.Get(this.Bot.Guilds[guild.Id]).Build(new TVar("Level", this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level))}"
: $":stars: {this.tKey.GainedLevel.Get(this.Bot.Guilds[guild.Id]).Build(new TVar("User", user.Mention), new TVar("Count", this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level - PreviousLevel))}\n" +
$"{this.tKey.NewLevel.Get(this.Bot.Guilds[guild.Id]).Build(new TVar("Level", this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level))}";
var delete_delay = 10000;
if (this.Bot.Guilds[guild.Id].LevelRewards.Any(x => x.Level <= this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level))
{
build += "\n\n";
foreach (var reward in this.Bot.Guilds[guild.Id].LevelRewards.ToList().Where(x => x.Level <= this.Bot.Guilds[guild.Id].Members[user.Id].Experience.Level))
{
if (!guild.Roles.ContainsKey(reward.RoleId))
{
this.Bot.Guilds[guild.Id].LevelRewards = this.Bot.Guilds[guild.Id].LevelRewards.Remove(x => x.RoleId.ToString(), reward);
continue;
}
if (user.Roles.Any(x => x.Id == reward.RoleId))
continue;
delete_delay = 20000;
await user.GrantRoleAsync(guild.GetRole(reward.RoleId));
build += $"`{reward.Message.Replace("##Role##", $"{guild.GetRole(reward.RoleId).Name}").SanitizeForCode()}`\n";
}
}
embed = new DiscordEmbedBuilder
{
Author = new DiscordEmbedBuilder.EmbedAuthor
{
IconUrl = guild.IconUrl,
Name = guild.Name
},
Title = "",
Description = build,
Timestamp = DateTime.UtcNow,
Color = new DiscordColor("#4287f5"),
Thumbnail = new DiscordEmbedBuilder.EmbedThumbnail { Url = user.AvatarUrl },
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = this.tKey.AutomaticDeletion.Get(this.Bot.Guilds[guild.Id]).Build(new TVar("Seconds", delete_delay / 1000))
}
};
if (channel is not null)
{
_ = channel.SendMessageAsync($"{user.Mention}", embed).ContinueWith(async x =>
{
if (!x.IsCompletedSuccessfully)
return;
await Task.Delay(delete_delay);
_ = (await x).DeleteAsync();
});
}
else
{
DiscordMessage msg;
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
{
_ = Task.Run(async () =>
{
if (msg.Id == e.Message.Id)
{
this.Bot.DiscordClient.ComponentInteractionCreated -= RunInteraction;
this.Bot.Users[user.Id].ExperienceUser.DirectMessageOptOut = true;
_ = await msg.ModifyAsync(new DiscordMessageBuilder().AddEmbed(embed));
_ = await (await user.CreateDmChannelAsync()).SendMessageAsync(this.tKey.AutomaticDeletion.Get(this.Bot.Users[user.Id]).Build(
new TVar("Command", "`/levelrewards-optin`"),
new TVar("Bot", guild.CurrentMember.Mention)));
}
}).Add(this.Bot);
}
IEnumerable<DiscordComponent> discordComponents = new List<DiscordComponent>
{
{ new DiscordButtonComponent(ButtonStyle.Secondary, "opt-out-experience-dm", this.tKey.DisableDirectMessages.Get(this.Bot.Users[user.Id]), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⛔"))) },
};
msg = await (await user.CreateDmChannelAsync()).SendMessageAsync(new DiscordMessageBuilder().AddEmbed(embed).AddComponents(discordComponents));
this.Bot.DiscordClient.ComponentInteractionCreated += RunInteraction;
try
{
await Task.Delay(3600000);
embed.Footer.Text += $" • {this.t.Commands.Common.InteractionTimeout.Get(this.Bot.Users[user.Id])}";
_ = await msg.ModifyAsync(new DiscordMessageBuilder().AddEmbed(embed));
this.Bot.DiscordClient.ComponentInteractionCreated -= RunInteraction;
}
catch { }
}
}
}
}
internal void CheckExperience(ulong user, DiscordGuild guild)
{
var PreviousRequiredRepuationForNextLevel = this.CalculateLevelRequirement(this.Bot.Guilds[guild.Id].Members[user].Experience.Level - 1);
var RequiredRepuationForNextLevel = this.CalculateLevelRequirement(this.Bot.Guilds[guild.Id].Members[user].Experience.Level);
while (RequiredRepuationForNextLevel <= this.Bot.Guilds[guild.Id].Members[user].Experience.Points)
{
this.Bot.Guilds[guild.Id].Members[user].Experience.Level++;
PreviousRequiredRepuationForNextLevel = this.CalculateLevelRequirement(this.Bot.Guilds[guild.Id].Members[user].Experience.Level - 1);
RequiredRepuationForNextLevel = this.CalculateLevelRequirement(this.Bot.Guilds[guild.Id].Members[user].Experience.Level);
}
while (PreviousRequiredRepuationForNextLevel >= this.Bot.Guilds[guild.Id].Members[user].Experience.Points)
{
this.Bot.Guilds[guild.Id].Members[user].Experience.Level--;
PreviousRequiredRepuationForNextLevel = this.CalculateLevelRequirement(this.Bot.Guilds[guild.Id].Members[user].Experience.Level - 1);
}
}
internal long CalculateLevelRequirement(long Level)
{
if (!this.LevelCache.TryGetValue(Level, out var value))
{
var v = (long)Math.Ceiling(Math.Pow((double)Level, 1.60) * 92);
value = v;
this.LevelCache.TryAdd(Level, value);
}
return value;
}
}