27a25d90fc
BotFeature is a new base class that all new individual bot features will derive from. At least one new feature is planned for this bot, and in time it may be opened up so external assemblies can be loaded. Full list of changes: -Added BotFeature and ConfigSectionAttribute classes -Renamed ConfigLoader to Configuration -Removed RegexResponder specific configuration data -Added per-feature configuration data storage -LoadInitialConfig() no longer loads all configuration -ReloadServerConfig() now loads remaining configuration, and allows for actual configuration reloading. Live configuration reloading is not an exposed feature yet. -Can now delegate feature-specific configuration loading to feature classes that make use of ConfigSectionAttribute -RegexResponder fully implements BotFeature -Rule configuration loading moved to RegexResponder -Logging output has changed slightly in regards to rule triggering and execution -Changed configuration load behavior on startup -Version pushed up to 1.0.0
265 lines
8.9 KiB
C#
265 lines
8.9 KiB
C#
using Discord;
|
||
using Discord.WebSocket;
|
||
using System;
|
||
using System.Collections.ObjectModel;
|
||
using System.Diagnostics;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Noikoio.RegexBot.Feature.RegexResponder
|
||
{
|
||
// Contains code for handling each response in a rule.
|
||
partial class EventProcessor
|
||
{
|
||
private delegate Task ResponseProcessor(string cmd, RuleConfig r, SocketMessage m);
|
||
private readonly ReadOnlyDictionary<string, ResponseProcessor> _commands;
|
||
|
||
#if DEBUG
|
||
/// <summary>
|
||
/// Throws an exception. Meant to be a quick error handling test.
|
||
/// No parameters.
|
||
/// </summary>
|
||
private Task RP_Crash(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
throw new Exception("Requested in response.");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Prints all guild values (IDs for users, channels, roles) to console.
|
||
/// The guild info displayed is the one in which the command is invoked.
|
||
/// No parameters.
|
||
/// </summary>
|
||
private Task RP_DumpID(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
var g = ((SocketGuildUser)m.Author).Guild;
|
||
var result = new StringBuilder();
|
||
|
||
result.AppendLine("Users:");
|
||
foreach (var item in g.Users)
|
||
result.AppendLine($"{item.Id} {item.Username}#{item.Discriminator}");
|
||
result.AppendLine();
|
||
|
||
result.AppendLine("Channels:");
|
||
foreach (var item in g.Channels) result.AppendLine($"{item.Id} #{item.Name}");
|
||
result.AppendLine();
|
||
result.AppendLine("Roles:");
|
||
foreach (var item in g.Roles) result.AppendLine($"{item.Id} {item.Name}");
|
||
result.AppendLine();
|
||
|
||
Console.WriteLine(result.ToString());
|
||
return Task.CompletedTask;
|
||
}
|
||
#endif
|
||
/// <summary>
|
||
/// Sends a message to a specified channel.
|
||
/// Parameters: say (channel) (message)
|
||
/// </summary>
|
||
private async Task RP_Say(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
string[] @in = SplitParams(cmd, 3);
|
||
if (@in.Length != 3)
|
||
{
|
||
await Log("Error: say: Incorrect number of parameters.");
|
||
return;
|
||
}
|
||
|
||
var target = await GetMessageTargetAsync(@in[1], m);
|
||
if (target == null)
|
||
{
|
||
await Log("Error: say: Unable to resolve given target.");
|
||
return;
|
||
}
|
||
|
||
// CHANGE THE SAY
|
||
@in[2] = ProcessText(@in[2], m);
|
||
await target.SendMessageAsync(@in[2]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Reports the incoming message to a given channel.
|
||
/// Parameters: report (channel)
|
||
/// </summary>
|
||
private async Task RP_Report(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
string[] @in = SplitParams(cmd);
|
||
if (@in.Length != 2)
|
||
{
|
||
await Log("Error: report: Incorrect number of parameters.");
|
||
return;
|
||
}
|
||
|
||
var target = await GetMessageTargetAsync(@in[1], m);
|
||
if (target == null)
|
||
{
|
||
await Log("Error: report: Unable to resolve given target.");
|
||
return;
|
||
}
|
||
|
||
|
||
var responsefield = new StringBuilder();
|
||
responsefield.AppendLine("```");
|
||
foreach (var line in r.Responses)
|
||
responsefield.AppendLine(line.Replace("\r", "").Replace("\n", "\\n"));
|
||
responsefield.Append("```");
|
||
await target.SendMessageAsync("", embed: new EmbedBuilder()
|
||
{
|
||
Color = new Color(0xEDCE00), // configurable later?
|
||
|
||
Author = new EmbedAuthorBuilder()
|
||
{
|
||
Name = $"{m.Author.Username}#{m.Author.Discriminator} said:",
|
||
IconUrl = m.Author.GetAvatarUrl()
|
||
},
|
||
Description = m.Content,
|
||
|
||
Footer = new EmbedFooterBuilder()
|
||
{
|
||
Text = $"Rule '{r.DisplayName}'",
|
||
IconUrl = _client.CurrentUser.GetAvatarUrl()
|
||
},
|
||
Timestamp = m.Timestamp
|
||
}.AddField(new EmbedFieldBuilder()
|
||
{
|
||
Name = "Additional info",
|
||
Value = $"Channel: <#{m.Channel.Id}>\n" // NOTE: manually mentioning channel here
|
||
+ $"Username: {m.Author.Mention}\n"
|
||
+ $"Message ID: {m.Id}"
|
||
}).AddField(new EmbedFieldBuilder()
|
||
{
|
||
Name = "Executing response:",
|
||
Value = responsefield.ToString()
|
||
}));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Deletes the incoming message.
|
||
/// No parameters.
|
||
/// </summary>
|
||
private async Task RP_Remove(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
// Parameters are not checked
|
||
await m.DeleteAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Executes an external program and sends standard output to the given channel.
|
||
/// Parameters: exec (channel) (command line)
|
||
/// </summary>
|
||
private async Task RP_Exec(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
var @in = SplitParams(cmd, 4);
|
||
if (@in.Length < 3)
|
||
{
|
||
await Log("exec: Incorrect number of parameters.");
|
||
}
|
||
|
||
string result;
|
||
var target = await GetMessageTargetAsync(@in[1], m);
|
||
if (target == null)
|
||
{
|
||
await Log("Error: exec: Unable to resolve given channel.");
|
||
return;
|
||
}
|
||
|
||
ProcessStartInfo ps = new ProcessStartInfo()
|
||
{
|
||
FileName = @in[2],
|
||
Arguments = (@in.Length > 3 ? @in[3] : ""),
|
||
UseShellExecute = false,
|
||
RedirectStandardOutput = true
|
||
};
|
||
using (Process p = Process.Start(ps))
|
||
{
|
||
p.WaitForExit(5000); // waiting at most 5 seconds
|
||
if (p.HasExited)
|
||
{
|
||
if (p.ExitCode != 0) await Log("exec: Process returned exit code " + p.ExitCode);
|
||
using (var stdout = p.StandardOutput)
|
||
{
|
||
result = await stdout.ReadToEndAsync();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
await Log("exec: Process is taking too long to exit. Killing process.");
|
||
p.Kill();
|
||
return;
|
||
}
|
||
}
|
||
|
||
result = ProcessText(result.Trim(), m);
|
||
await target.SendMessageAsync(result);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Bans the sender of the incoming message.
|
||
/// No parameters.
|
||
/// </summary>
|
||
// TODO add parameter for message auto-deleting
|
||
private async Task RP_Ban(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
SocketGuild g = ((SocketGuildUser)m.Author).Guild;
|
||
await g.AddBanAsync(m.Author);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Grants or revokes a specified role to/from a given user.
|
||
/// Parameters: grantrole/revokerole (user ID or @_) (role ID)
|
||
/// </summary>
|
||
private async Task RP_GrantRevokeRole(string cmd, RuleConfig r, SocketMessage m)
|
||
{
|
||
string[] @in = SplitParams(cmd);
|
||
if (@in.Length != 3)
|
||
{
|
||
await Log($"Error: {@in[0]}: incorrect number of parameters.");
|
||
return;
|
||
}
|
||
if (!ulong.TryParse(@in[2], out var roleID))
|
||
{
|
||
await Log($"Error: {@in[0]}: Invalid role ID specified.");
|
||
return;
|
||
}
|
||
|
||
// Finding role
|
||
var gu = (SocketGuildUser)m.Author;
|
||
SocketRole rl = gu.Guild.GetRole(roleID);
|
||
if (rl == null)
|
||
{
|
||
await Log($"Error: {@in[0]}: Specified role not found.");
|
||
return;
|
||
}
|
||
|
||
// Finding user
|
||
SocketGuildUser target;
|
||
if (@in[1] == "@_")
|
||
{
|
||
target = gu;
|
||
}
|
||
else
|
||
{
|
||
if (!ulong.TryParse(@in[1], out var userID))
|
||
{
|
||
await Log($"Error: {@in[0]}: Invalid user ID specified.");
|
||
return;
|
||
}
|
||
target = gu.Guild.GetUser(userID);
|
||
if (target == null)
|
||
{
|
||
await Log($"Error: {@in[0]}: Given user ID does not exist in this server.");
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (@in[0].ToLower() == "grantrole")
|
||
{
|
||
await target.AddRoleAsync(rl);
|
||
}
|
||
else
|
||
{
|
||
await target.RemoveRoleAsync(rl);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|