Skip to content

Commit

Permalink
New feature for auto assign ranks by the users themselves
Browse files Browse the repository at this point in the history
  • Loading branch information
Smieszkokoleszko committed Mar 24, 2024
1 parent 93a32f8 commit 633235c
Show file tree
Hide file tree
Showing 6 changed files with 452 additions and 209 deletions.
4 changes: 3 additions & 1 deletion ArmaforcesMissionBot/Attributes/RequireRankAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace ArmaforcesMissionBot.Attributes
{
public enum RanksEnum
{
Recruiter
Recruiter,
RoleMaker
}

internal static class RanksEnumMethods
Expand All @@ -19,6 +20,7 @@ public static ulong GetID(this RanksEnum role, Config config)
=> role switch
{
RanksEnum.Recruiter => config.RecruiterRole,
RanksEnum.RoleMaker => config.RoleMaker,
_ => 0
};
}
Expand Down
2 changes: 2 additions & 0 deletions ArmaforcesMissionBot/DataClasses/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class Config
public ulong HallOfShameChannel { get; set; }
public ulong RecruitInfoChannel { get; set; }
public ulong RecruitAskChannel { get; set; }
public ulong RoleMaker { get; set; }
public ulong RoleAssignChannel { get; set; }

public void Load()
{
Expand Down
320 changes: 320 additions & 0 deletions ArmaforcesMissionBot/Handlers/ReactionHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
using ArmaforcesMissionBot.DataClasses;
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Timers;
using ArmaforcesMissionBot.Helpers;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static ArmaforcesMissionBotSharedClasses.Mission;
using Microsoft.CodeAnalysis.Differencing;
using CSharpFunctionalExtensions;

namespace ArmaforcesMissionBot.Handlers
{
public static class StringExtensionMethods
{
public static string ReplaceFirst(this string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
}
public class ReactionHandler : IInstallable
{
private DiscordSocketClient _client;
private MiscHelper _miscHelper;
private IServiceProvider _services;
private Config _config;
private Timer _timer;

public async Task Install(IServiceProvider map)
{
_client = map.GetService<DiscordSocketClient>();
_config = map.GetService<Config>();
_miscHelper = map.GetService<MiscHelper>();
_services = map;
// Hook the MessageReceived event into our command handler
_client.ReactionAdded += HandleReactionAdded;
_client.ReactionRemoved += HandleReactionRemoved;

_timer = new Timer();
_timer.Interval = 2000;

_timer.Elapsed += CheckReactionTimes;
_timer.AutoReset = true;
_timer.Enabled = true;
}

private async Task HandleReactionAdded(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
{
if (reaction.User.IsSpecified && !reaction.User.Value.IsBot && _config.RoleAssignChannel == channel.Id)
{
var fullMessage = await channel.GetMessageAsync(message.Id) as IUserMessage;

var messageDescription = fullMessage.Embeds.Single().Description;
ulong rank;
var reactionsMatches = MiscHelper.GetRankArrayMatchesFromText(messageDescription);
var rankForEmoji = reactionsMatches.Find(x => x.Item1 == reaction.Emote.ToString());

try
{
string regexPattern = @"\<\@\&([0-9]+)\>";
var rankMatches = Regex.Matches(rankForEmoji.Item2, regexPattern, RegexOptions.IgnoreCase | RegexOptions.RightToLeft).First().ToString();
rank = ulong.Parse(Regex.Matches(rankMatches, "[0-9]+", RegexOptions.IgnoreCase | RegexOptions.RightToLeft).First().Value);
}
catch (Exception e)
{
Console.WriteLine($"Parsing ranks failed.");
return;
}

try
{
var role = (channel as SocketGuildChannel).Guild.GetRole(rank);

await (reaction.User.Value as SocketGuildUser).AddRoleAsync(role);

}
catch (Exception ex)
{
Console.WriteLine($"Adding rank failed.");
}

return;
}


var signups = _services.GetService<SignupsData>();

var reactionStringAnimatedVersion = reaction.Emote.ToString().Insert(1, "a");

if (reaction.User.IsSpecified && !reaction.User.Value.IsBot && signups.Missions.Any(x => x.SignupChannel == channel.Id))
{
var mission = signups.Missions.Single(x => x.SignupChannel == channel.Id);

await HandleReactionChange(message, channel, reaction, signups);
Console.WriteLine($"[{DateTime.Now.ToString()}] {reaction.User.Value.Username} added reaction {reaction.Emote.Name}");

if (signups.SignupBans.ContainsKey(reaction.User.Value.Id) && signups.SignupBans[reaction.User.Value.Id] > mission.Date)
{
await reaction.User.Value.SendMessageAsync("Masz bana na zapisy, nie mo¿esz zapisaæ siê na misjê, która odbêdzie siê w czasie trwania bana.");
var teamMsg = await channel.GetMessageAsync(message.Id) as IUserMessage;
await teamMsg.RemoveReactionAsync(reaction.Emote, reaction.User.Value);
return;
}

await mission.Access.WaitAsync(-1);
try
{
if (mission.Teams.Any(x => x.TeamMsg == message.Id))
{
var team = mission.Teams.Single(x => x.TeamMsg == message.Id);
if (team.Slots.Any(x => (x.Emoji == reaction.Emote.ToString() || x.Emoji == reactionStringAnimatedVersion) && (x.Count > x.Signed.Count() || team.Reserve != 0)))
{
var teamMsg = await channel.GetMessageAsync(message.Id) as IUserMessage;

var embed = teamMsg.Embeds.Single();

if (!mission.SignedUsers.Any(x => x == reaction.User.Value.Id))
{
var slot = team.Slots.Single(x => x.Emoji == reaction.Emote.ToString() || x.Emoji == reactionStringAnimatedVersion);
if (!slot.Signed.Contains(reaction.User.Value.Id))
slot.Signed.Add(reaction.User.Value.Id);
if (!mission.SignedUsers.Contains(reaction.User.Value.Id))
mission.SignedUsers.Add(reaction.User.Value.Id);

var newDescription = _miscHelper.BuildTeamSlots(team);

var newEmbed = new EmbedBuilder
{
Title = team.Name,
Color = embed.Color
};

if (newDescription.Count == 2)
newEmbed.WithDescription(newDescription[0] + newDescription[1]);
else if (newDescription.Count == 1)
newEmbed.WithDescription(newDescription[0]);

if (embed.Footer.HasValue)
newEmbed.WithFooter(embed.Footer.Value.Text);
else
newEmbed.WithFooter(team.Pattern);

await teamMsg.ModifyAsync(x => x.Embed = newEmbed.Build());
}
else
{
await teamMsg.RemoveReactionAsync(reaction.Emote, reaction.User.Value);
}
}
else if (team.Slots.Any(x => x.Emoji == reaction.Emote.ToString()))
{
var teamMsg = await channel.GetMessageAsync(message.Id) as IUserMessage;
await teamMsg.RemoveReactionAsync(reaction.Emote, reaction.User.Value);
}
}
}
finally
{
mission.Access.Release();
}
}
else if (signups.Missions.Any(x => x.SignupChannel == channel.Id) && reaction.UserId != _client.CurrentUser.Id)
{
var user = _client.GetUser(reaction.UserId);
Console.WriteLine($"Naprawiam reakcje po spamie {user.Username}");
var teamMsg = await channel.GetMessageAsync(message.Id) as IUserMessage;
await teamMsg.RemoveReactionAsync(reaction.Emote, user);
}
}

private async Task HandleReactionRemoved(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
{
if (reaction.User.IsSpecified && !reaction.User.Value.IsBot && _config.RoleAssignChannel == channel.Id)
{
var fullMessage = await channel.GetMessageAsync(message.Id) as IUserMessage;

var messageDescription = fullMessage.Embeds.Single().Description;
ulong rank;
var reactionsMatches = MiscHelper.GetRankArrayMatchesFromText(messageDescription);
var rankForEmoji = reactionsMatches.Find(x => x.Item1 == reaction.Emote.ToString());

try
{
string regexPattern = @"\<\@\&([0-9]+)\>";
var rankMatches = Regex.Matches(rankForEmoji.Item2, regexPattern, RegexOptions.IgnoreCase | RegexOptions.RightToLeft).First().ToString();
rank = ulong.Parse(Regex.Matches(rankMatches, "[0-9]+", RegexOptions.IgnoreCase | RegexOptions.RightToLeft).First().Value);
}
catch (Exception e)
{
Console.WriteLine($"Parsing ranks failed.");
return;
}

try
{
var role = (channel as SocketGuildChannel).Guild.GetRole(rank);

await (reaction.User.Value as SocketGuildUser).RemoveRoleAsync(role);

}
catch (Exception ex)
{
Console.WriteLine($"Adding rank failed.");
}

return;
}


var signups = _services.GetService<SignupsData>();

var reactionStringAnimatedVersion = reaction.Emote.ToString().Insert(1, "a");

if (signups.Missions.Any(x => x.SignupChannel == channel.Id))
{
var mission = signups.Missions.Single(x => x.SignupChannel == channel.Id);
var user = await (channel as IGuildChannel).Guild.GetUserAsync(reaction.UserId);

Console.WriteLine($"[{DateTime.Now.ToString()}] {user.Username} removed reaction {reaction.Emote.Name}");

await mission.Access.WaitAsync(-1);
try
{
if (mission.Teams.Any(x => x.TeamMsg == message.Id))
{
var team = mission.Teams.Single(x => x.TeamMsg == message.Id);
if (team.Slots.Any(x => (x.Emoji == reaction.Emote.ToString() || x.Emoji == reactionStringAnimatedVersion) && x.Signed.Contains(user.Id)))
{
var teamMsg = await channel.GetMessageAsync(message.Id) as IUserMessage;
var embed = teamMsg.Embeds.Single();

var slot = team.Slots.Single(x => x.Emoji == reaction.Emote.ToString() || x.Emoji == reactionStringAnimatedVersion);
slot.Signed.Remove(user.Id);
mission.SignedUsers.Remove(user.Id);

var newDescription = _miscHelper.BuildTeamSlots(team);

var newEmbed = new EmbedBuilder
{
Title = team.Name,
Color = embed.Color
};

if (newDescription.Count == 2)
newEmbed.WithDescription(newDescription[0] + newDescription[1]);
else if (newDescription.Count == 1)
newEmbed.WithDescription(newDescription[0]);

if (embed.Footer.HasValue)
newEmbed.WithFooter(embed.Footer.Value.Text);
else
newEmbed.WithFooter(team.Pattern);

await teamMsg.ModifyAsync(x => x.Embed = newEmbed.Build());
}
}
}
finally
{
mission.Access.Release();
}
}
}

private async Task HandleReactionChange(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction, SignupsData signups)
{
await signups.BanAccess.WaitAsync(-1);
try
{
if (!signups.ReactionTimes.ContainsKey(reaction.User.Value.Id))
{
signups.ReactionTimes[reaction.User.Value.Id] = new Queue<DateTime>();
}

signups.ReactionTimes[reaction.User.Value.Id].Enqueue(DateTime.Now);

Console.WriteLine($"[{DateTime.Now.ToString()}] {reaction.User.Value.Username} spam counter: {signups.ReactionTimes[reaction.User.Value.Id].Count}");

if (signups.ReactionTimes[reaction.User.Value.Id].Count >= 10 && !signups.SpamBans.ContainsKey(reaction.User.Value.Id))
{
await Helpers.BanHelper.BanUserSpam(_services, reaction.User.Value);
}
}
finally
{
signups.BanAccess.Release();
}
}

private async void CheckReactionTimes(object source, ElapsedEventArgs e)
{
var signups = _services.GetService<SignupsData>();

await signups.BanAccess.WaitAsync(-1);
try
{
foreach (var user in signups.ReactionTimes)
{
while (user.Value.Count > 0 && user.Value.Peek() < e.SignalTime.AddSeconds(-30))
user.Value.Dequeue();
}
}
finally
{
signups.BanAccess.Release();
}
}
}
}
Loading

0 comments on commit 633235c

Please sign in to comment.