using Discord;
using Discord.WebSocket;
using Newtonsoft.Json.Linq;
using Noikoio.RegexBot.ConfigItem;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
namespace Noikoio.RegexBot.Feature.RegexResponder
{
///
/// Implements per-message regex matching and executes customizable responses.
/// Namesake of this project.
///
partial class EventProcessor : BotFeature
{
private readonly DiscordSocketClient _client;
public override string Name => "RegexResponder";
public EventProcessor(DiscordSocketClient client) : base(client)
{
_client = client;
_client.MessageReceived += OnMessageReceived;
_client.MessageUpdated += OnMessageUpdated;
_commands = new ReadOnlyDictionary(
new Dictionary() {
#if DEBUG
{ "crash", RP_Crash },
{ "dumpid", RP_DumpID },
#endif
{ "report", RP_Report },
{ "say", RP_Say },
{ "remove", RP_Remove },
{ "delete", RP_Remove },
{ "erase", RP_Remove },
{ "exec", RP_Exec },
{ "ban", RP_Ban },
{ "grantrole", RP_GrantRevokeRole },
{ "revokerole", RP_GrantRevokeRole }
}
);
}
#region Event handlers
private async Task OnMessageReceived(SocketMessage arg)
=> await ReceiveMessage(arg);
private async Task OnMessageUpdated(Cacheable arg1, SocketMessage arg2, ISocketMessageChannel arg3)
=> await ReceiveMessage(arg2);
#endregion
///
/// Receives incoming messages and creates tasks to handle them if necessary.
///
private async Task ReceiveMessage(SocketMessage arg)
{
// Determine channel type - if not a guild channel, stop.
var ch = arg.Channel as SocketGuildChannel;
if (ch == null) return;
if (arg.Author == _client.CurrentUser) return; // Don't ever self-trigger
// Looking up server information and extracting settings
SocketGuild g = ch.Guild;
ServerConfig sd = null;
foreach (var item in RegexBot.Config.Servers)
{
if (item.Id.HasValue)
{
// Finding server by ID
if (g.Id == item.Id)
{
sd = item;
break;
}
}
else
{
// Finding server by name and caching ID
if (string.Equals(item.Name, g.Name, StringComparison.OrdinalIgnoreCase))
{
item.Id = g.Id;
sd = item;
await Logger.GetLogger(Configuration.LogPrefix)
($"Suggestion: Server \"{item.Name}\" can be defined as \"{item.Id}::{item.Name}\"");
break;
}
}
}
if (sd == null) return; // No server configuration found
var rules = GetConfig(ch.Guild.Id) as IEnumerable;
if (rules == null) return;
// Further processing is sent to the thread pool
foreach (var rule in rules)
await Task.Run(async () => await ProcessMessage(sd, rule, arg));
}
///
/// Uses information from a single rule and checks if the incoming message is a match.
/// If it matches, the rule's responses are executed. To be run in the thread pool.
///
private async Task ProcessMessage(ServerConfig srv, RuleConfig rule, SocketMessage msg)
{
string msgcontent;
// Embed mode?
if (rule.MatchEmbeds)
{
var embeds = new StringBuilder();
foreach (var e in msg.Embeds) embeds.AppendLine(EmbedToString(e));
msgcontent = embeds.ToString();
}
else
{
msgcontent = msg.Content;
}
// Min/max message length check
if (rule.MinLength.HasValue && msgcontent.Length <= rule.MinLength.Value) return;
if (rule.MaxLength.HasValue && msgcontent.Length >= rule.MaxLength.Value) return;
// Moderator bypass check
if (rule.AllowModBypass == true && srv.Moderators.ExistsInList(msg)) return;
// Individual rule filtering check
if (IsFiltered(rule, msg)) return;
// And finally, pattern matching checks
bool success = false;
foreach (var regex in rule.Regex)
{
success = regex.Match(msgcontent).Success;
if (success) break;
}
if (!success) return;
// Prepare to execute responses
await Log($"\"{rule.DisplayName}\" triggered in {srv.Name}/#{msg.Channel} by {msg.Author.ToString()}");
foreach (string rcmd in rule.Responses)
{
string cmd = rcmd.TrimStart(' ').Split(' ')[0].ToLower();
try
{
ResponseProcessor response;
if (!_commands.TryGetValue(cmd, out response))
{
await Log($"Unknown command defined in response: \"{cmd}\"");
continue;
}
await response.Invoke(rcmd, rule, msg);
}
catch (Exception ex)
{
await Log($"Encountered an error while processing \"{cmd}\". Details follow:");
await Log(ex.ToString());
}
}
}
[ConfigSection("rules")]
public override async Task