Several fixes after a round of testing

Fixed the following compilation errors:
-Moderators collection not initialized
-Outdated method signatures for ban and kick in ModuleBase
-Update author name in manifests
-Fixed incorrect method signature in AutoScriptResponder

Minor improvements:
-Updated external dependencies
-Remove unused variables in ConfDefinition of RegexModerator
-Improve parallel execution of matches?
-Send exception message on logging failure to reporting channel
-Slightly change ModuleLoader logging output
-Add Discord.Net unhandled exception output to logging
-Updated link to Github
-Changed GuildState exception handling message for conciseness

Fixes:
-SQL index creation in LoggingService
-SQL view creation in UserCacheService
-Add casts from ulong to long in SQL inserts
-External modules no longer loaded twice
-Non-Info messages from Discord.Net will now be reported
-User data had not been recorded at proper times
-Some modules had been returning null instead of Task with null
-Guild state exception handling should not have handled config errors
This commit is contained in:
Noikoio 2019-06-21 15:05:58 -07:00
parent 2319c91fc7
commit ffa5b5754b
14 changed files with 63 additions and 69 deletions

View file

@ -46,7 +46,7 @@ namespace Kerobot
// Everything's ready to go. Print the welcome message here. // Everything's ready to go. Print the welcome message here.
var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
InstanceLogAsync(false, "Kerobot", InstanceLogAsync(false, "Kerobot",
$"This is Kerobot v{ver.ToString(3)}. https://github.com/Noikoio/Kerobot").Wait(); $"This is Kerobot v{ver.ToString(3)}. https://github.com/Noiiko/Kerobot").Wait();
// We return to Program.cs at this point. // We return to Program.cs at this point.
} }

View file

@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<StartupObject>Kerobot.Program</StartupObject> <StartupObject>Kerobot.Program</StartupObject>
<AssemblyVersion>0.0.1</AssemblyVersion> <Authors>Noiiko</Authors>
<Authors>Noikoio</Authors>
<Company /> <Company />
<Description>Advanced and flexible Discord moderation bot.</Description> <Description>Advanced and flexible Discord moderation bot.</Description>
<FileVersion>0.0.1</FileVersion>
<Version>0.0.1</Version> <Version>0.0.1</Version>
<LangVersion>7.2</LangVersion> <LangVersion>7.2</LangVersion>
</PropertyGroup> </PropertyGroup>
@ -27,10 +25,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.4.3" /> <PackageReference Include="CommandLineParser" Version="2.5.0" />
<PackageReference Include="Discord.Net" Version="2.0.1" /> <PackageReference Include="Discord.Net" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Npgsql" Version="4.0.5" /> <PackageReference Include="Npgsql" Version="4.0.7" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -88,23 +88,20 @@ namespace Kerobot
/// <param name="targetUser">The user which to perform the action to.</param> /// <param name="targetUser">The user which to perform the action to.</param>
/// <param name="purgeDays">Number of days of prior post history to delete on ban. Must be between 0-7.</param> /// <param name="purgeDays">Number of days of prior post history to delete on ban. Must be between 0-7.</param>
/// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param> /// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param>
/// <param name="dmMsg"> /// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action being taken.</param>
/// Message to DM the target user. Set null to disable. Instances of "%s" are replaced with the guild name protected Task<BanKickResult> BanAsync(SocketGuild guild, string source, ulong targetUser, int purgeDays, string reason, bool sendDMToTarget)
/// and instances of "%r" are replaced with the reason. => Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, purgeDays, reason, sendDMToTarget);
/// </param>
protected Task<BanKickResult> BanAsync(SocketGuild guild, string source, ulong targetUser, int purgeDays, string reason, string dmMsg)
=> Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, purgeDays, reason, dmMsg);
/// <summary> /// <summary>
/// Similar to <see cref="BanAsync(SocketGuild, string, ulong, int, string, string)"/>, but making use of an /// Similar to <see cref="BanAsync(SocketGuild, string, ulong, int, string, bool)"/>, but making use of an
/// EntityCache lookup to determine the target. /// EntityCache lookup to determine the target.
/// </summary> /// </summary>
/// <param name="targetSearch">The EntityCache search string.</param> /// <param name="targetSearch">The EntityCache search string.</param>
protected async Task<BanKickResult> BanAsync(SocketGuild guild, string source, string targetSearch, int purgeDays, string reason, string dmMsg) protected async Task<BanKickResult> BanAsync(SocketGuild guild, string source, string targetSearch, int purgeDays, string reason, bool sendDMToTarget)
{ {
var result = await Kerobot.EcQueryUser(guild.Id, targetSearch); var result = await Kerobot.EcQueryUser(guild.Id, targetSearch);
if (result == null) return new BanKickResult(null, false, true); if (result == null) return new BanKickResult(null, false, true);
return await BanAsync(guild, source, result.UserID, purgeDays, reason, dmMsg); return await BanAsync(guild, source, result.UserID, purgeDays, reason, sendDMToTarget);
} }
/// <summary> /// <summary>
@ -117,23 +114,20 @@ namespace Kerobot
/// <param name="source">The user, if any, which requested the action to be taken.</param> /// <param name="source">The user, if any, which requested the action to be taken.</param>
/// <param name="targetUser">The user which to perform the action to.</param> /// <param name="targetUser">The user which to perform the action to.</param>
/// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param> /// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param>
/// <param name="dmMsg"> /// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action being taken.</param>
/// Message to DM the target user. Set null to disable. Instances of "%s" are replaced with the guild name protected Task<BanKickResult> KickAsync(SocketGuild guild, string source, ulong targetUser, string reason, bool sendDMToTarget)
/// and instances of "%r" are replaced with the reason. => Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, 0, reason, sendDMToTarget);
/// </param>
protected Task<BanKickResult> KickAsync(SocketGuild guild, string source, ulong targetUser, string reason, string dmMsg)
=> Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, 0, reason, dmMsg);
/// <summary> /// <summary>
/// Similar to <see cref="KickAsync(SocketGuild, string, ulong, string, string)"/>, but making use of an /// Similar to <see cref="KickAsync(SocketGuild, string, ulong, string, bool)"/>, but making use of an
/// EntityCache lookup to determine the target. /// EntityCache lookup to determine the target.
/// </summary> /// </summary>
/// <param name="targetSearch">The EntityCache search string.</param> /// <param name="targetSearch">The EntityCache search string.</param>
protected async Task<BanKickResult> KickAsync(SocketGuild guild, string source, string targetSearch, string reason, string dmMsg) protected async Task<BanKickResult> KickAsync(SocketGuild guild, string source, string targetSearch, string reason, bool sendDMToTarget)
{ {
var result = await Kerobot.EcQueryUser(guild.Id, targetSearch); var result = await Kerobot.EcQueryUser(guild.Id, targetSearch);
if (result == null) return new BanKickResult(null, false, true); if (result == null) return new BanKickResult(null, false, true);
return await KickAsync(guild, source, result.UserID, reason, dmMsg); return await KickAsync(guild, source, result.UserID, reason, sendDMToTarget);
} }
/// <summary> /// <summary>

View file

@ -45,7 +45,7 @@ namespace Kerobot
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
Environment.Exit(2); Environment.Exit(2);
} }
modules.AddRange(LoadModulesFromAssembly(a, k)); modules.AddRange(amods);
} }
return modules.AsReadOnly(); return modules.AsReadOnly();
} }
@ -56,15 +56,14 @@ namespace Kerobot
where !type.IsAssignableFrom(typeof(ModuleBase)) where !type.IsAssignableFrom(typeof(ModuleBase))
where type.GetCustomAttribute<KerobotModuleAttribute>() != null where type.GetCustomAttribute<KerobotModuleAttribute>() != null
select type; select type;
k.InstanceLogAsync(false, LogName, k.InstanceLogAsync(false, LogName, $"Scanning {asm.GetName().Name}");
$"{asm.GetName().Name} has {eligibleTypes.Count()} usable types");
var newmods = new List<ModuleBase>(); var newmods = new List<ModuleBase>();
foreach (var t in eligibleTypes) foreach (var t in eligibleTypes)
{ {
var mod = Activator.CreateInstance(t, k); var mod = Activator.CreateInstance(t, k);
k.InstanceLogAsync(false, LogName, k.InstanceLogAsync(false, LogName,
$"---> Instance created: {t.FullName}"); $"---> Loading module {t.FullName}");
newmods.Add((ModuleBase)mod); newmods.Add((ModuleBase)mod);
} }
return newmods; return newmods;

View file

@ -66,7 +66,7 @@ namespace Kerobot.Services.EntityCache
c.CommandText = $"create or replace view {UserView} as " + c.CommandText = $"create or replace view {UserView} as " +
$"select {GlobalUserTable}.user_id, {GuildUserTable}.guild_id, {GuildUserTable}.first_seen, " + $"select {GlobalUserTable}.user_id, {GuildUserTable}.guild_id, {GuildUserTable}.first_seen, " +
$"{GuildUserTable}.cache_update_time, " + $"{GuildUserTable}.cache_update_time, " +
$"{GlobalUserTable}.username, {GlobalUserTable}.discriminator, {GlobalUserTable}.nickname, " + $"{GlobalUserTable}.username, {GlobalUserTable}.discriminator, {GuildUserTable}.nickname, " +
$"{GlobalUserTable}.avatar_url " + $"{GlobalUserTable}.avatar_url " +
$"from {GlobalUserTable} join {GuildUserTable} on {GlobalUserTable}.user_id = {GuildUserTable}.user_id"; $"from {GlobalUserTable} join {GuildUserTable} on {GlobalUserTable}.user_id = {GuildUserTable}.user_id";
await c.ExecuteNonQueryAsync(); await c.ExecuteNonQueryAsync();
@ -90,7 +90,7 @@ namespace Kerobot.Services.EntityCache
"on conflict (user_id) do update " + "on conflict (user_id) do update " +
"set cache_update_time = EXCLUDED.cache_update_time, username = EXCLUDED.username, " + "set cache_update_time = EXCLUDED.cache_update_time, username = EXCLUDED.username, " +
"discriminator = EXCLUDED.discriminator, avatar_url = EXCLUDED.avatar_url"; "discriminator = EXCLUDED.discriminator, avatar_url = EXCLUDED.avatar_url";
c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = current.Id; c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = (long)current.Id;
c.Parameters.Add("@Uname", NpgsqlDbType.Text).Value = current.Username; c.Parameters.Add("@Uname", NpgsqlDbType.Text).Value = current.Username;
c.Parameters.Add("@Disc", NpgsqlDbType.Text).Value = current.Discriminator; c.Parameters.Add("@Disc", NpgsqlDbType.Text).Value = current.Discriminator;
var aurl = c.Parameters.Add("@Aurl", NpgsqlDbType.Text); var aurl = c.Parameters.Add("@Aurl", NpgsqlDbType.Text);
@ -106,6 +106,9 @@ namespace Kerobot.Services.EntityCache
private async Task DiscordClient_GuildMemberUpdated(SocketGuildUser old, SocketGuildUser current) private async Task DiscordClient_GuildMemberUpdated(SocketGuildUser old, SocketGuildUser current)
{ {
// Also update user data here, in case it's unknown (avoid foreign key constraint violation)
await DiscordClient_UserUpdated(old, current);
using (var db = await _kb.GetOpenNpgsqlConnectionAsync()) using (var db = await _kb.GetOpenNpgsqlConnectionAsync())
{ {
using (var c = db.CreateCommand()) using (var c = db.CreateCommand())
@ -113,11 +116,10 @@ namespace Kerobot.Services.EntityCache
c.CommandText = $"insert into {GuildUserTable} " + c.CommandText = $"insert into {GuildUserTable} " +
"(user_id, guild_id, cache_update_time, nickname) values " + "(user_id, guild_id, cache_update_time, nickname) values " +
"(@Uid, @Gid, now(), @Nname) " + "(@Uid, @Gid, now(), @Nname) " +
"on conflict (user_id) do update " + "on conflict (user_id, guild_id) do update " +
"set cache_update_time = EXCLUDED.cache_update_time, username = EXCLUDED.username, " + "set cache_update_time = EXCLUDED.cache_update_time, nickname = EXCLUDED.nickname";
"discriminator = EXCLUDED.discriminator, avatar_url = EXCLUDED.avatar_url"; c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = (long)current.Id;
c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = current.Id; c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = (long)current.Guild.Id;
c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = current.Guild.Id;
var nname = c.Parameters.Add("@Nname", NpgsqlDbType.Text); var nname = c.Parameters.Add("@Nname", NpgsqlDbType.Text);
if (current.Nickname != null) nname.Value = current.Nickname; if (current.Nickname != null) nname.Value = current.Nickname;
else nname.Value = DBNull.Value; else nname.Value = DBNull.Value;
@ -130,7 +132,7 @@ namespace Kerobot.Services.EntityCache
#endregion #endregion
#region Querying #region Querying
private static Regex DiscriminatorSearch = new Regex(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled); private static readonly Regex DiscriminatorSearch = new Regex(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled);
/// <summary> /// <summary>
/// See <see cref="Kerobot.EcQueryUser(ulong, string)"/>. /// See <see cref="Kerobot.EcQueryUser(ulong, string)"/>.
@ -174,12 +176,12 @@ namespace Kerobot.Services.EntityCache
var c = db.CreateCommand(); var c = db.CreateCommand();
c.CommandText = $"select * from {UserView} " + c.CommandText = $"select * from {UserView} " +
"where guild_id = @Gid"; "where guild_id = @Gid";
c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = (long)guildId;
if (sID.HasValue) if (sID.HasValue)
{ {
c.CommandText += " and user_id = @Uid"; c.CommandText += " and user_id = @Uid";
c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = sID.Value; c.Parameters.Add("@Uid", NpgsqlDbType.Bigint).Value = (long)sID.Value;
} }
if (sName != null) if (sName != null)

View file

@ -149,15 +149,15 @@ namespace Kerobot.Services.GuildState
var tn = t.Name; var tn = t.Name;
try try
{ {
object state;
try try
{ {
state = await mod.CreateGuildStateAsync(guildId, guildConf[tn]); // can be null var state = await mod.CreateGuildStateAsync(guildId, guildConf[tn]); // can be null
newStates.Add(t, new StateInfo(state, jstrHash));
} }
catch (Exception ex) catch (Exception ex) when (!(ex is ModuleLoadException))
{ {
Log("Encountered unhandled exception during guild state initialization:\n" + Log("Unhandled exception while initializing guild state for module:\n" +
$"Module: {tn}\n" + $"Module: {tn} | " +
$"Guild: {guildId} ({Kerobot.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" + $"Guild: {guildId} ({Kerobot.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" +
$"```\n{ex.ToString()}\n```", true).Wait(); $"```\n{ex.ToString()}\n```", true).Wait();
Kerobot.GuildLogAsync(guildId, GuildLogSource, Kerobot.GuildLogAsync(guildId, GuildLogSource,
@ -165,7 +165,6 @@ namespace Kerobot.Services.GuildState
"The bot owner has been notified.").Wait(); "The bot owner has been notified.").Wait();
return false; return false;
} }
newStates.Add(t, new StateInfo(state, jstrHash));
} }
catch (ModuleLoadException ex) catch (ModuleLoadException ex)
{ {

View file

@ -35,10 +35,10 @@ namespace Kerobot.Services.Logging
/// </summary> /// </summary>
private async Task DiscordClient_Log(LogMessage arg) private async Task DiscordClient_Log(LogMessage arg)
{ {
var ts = DateTimeOffset.UtcNow; bool important = arg.Severity != LogSeverity.Info;
bool important = arg.Severity > LogSeverity.Info;
string msg = $"[{Enum.GetName(typeof(LogSeverity), arg.Severity)}] {arg.Message}"; string msg = $"[{Enum.GetName(typeof(LogSeverity), arg.Severity)}] {arg.Message}";
const string logSource = "Discord.Net"; const string logSource = "Discord.Net";
if (arg.Exception != null) msg += "\n```\n" + arg.Exception.ToString() + "\n```";
if (important) await DoInstanceLogAsync(true, logSource, msg); if (important) await DoInstanceLogAsync(true, logSource, msg);
else FormatToConsole(DateTimeOffset.UtcNow, logSource, msg); else FormatToConsole(DateTimeOffset.UtcNow, logSource, msg);
@ -64,7 +64,7 @@ namespace Kerobot.Services.Logging
using (var c = db.CreateCommand()) using (var c = db.CreateCommand())
{ {
c.CommandText = "create index if not exists " + c.CommandText = "create index if not exists " +
$"{TableLog}_guildid_idx on {TableLog} guild_id"; $"{TableLog}_guild_id_idx on {TableLog} (guild_id)";
await c.ExecuteNonQueryAsync(); await c.ExecuteNonQueryAsync();
} }
} }
@ -78,8 +78,8 @@ namespace Kerobot.Services.Logging
{ {
c.CommandText = $"insert into {TableLog} (guild_id, log_timestamp, log_source, message) values" c.CommandText = $"insert into {TableLog} (guild_id, log_timestamp, log_source, message) values"
+ "(@Gid, @Ts, @Src, @Msg)"; + "(@Gid, @Ts, @Src, @Msg)";
c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = guildId; c.Parameters.Add("@Gid", NpgsqlDbType.Bigint).Value = (long)guildId;
c.Parameters.Add("@Ts", NpgsqlDbType.TimestampTZ).Value = timestamp; c.Parameters.Add("@Ts", NpgsqlDbType.TimestampTz).Value = timestamp;
c.Parameters.Add("@Src", NpgsqlDbType.Text).Value = source; c.Parameters.Add("@Src", NpgsqlDbType.Text).Value = source;
c.Parameters.Add("@Msg", NpgsqlDbType.Text).Value = message; c.Parameters.Add("@Msg", NpgsqlDbType.Text).Value = message;
c.Prepare(); c.Prepare();
@ -126,6 +126,7 @@ namespace Kerobot.Services.Logging
} }
// Report to logging channel if necessary and possible // Report to logging channel if necessary and possible
// TODO replace with webhook?
var (g, c) = Kerobot.Config.InstanceLogReportTarget; var (g, c) = Kerobot.Config.InstanceLogReportTarget;
if ((insertException != null || report) && if ((insertException != null || report) &&
g != 0 && c != 0 && Kerobot.DiscordClient.ConnectionState == ConnectionState.Connected) g != 0 && c != 0 && Kerobot.DiscordClient.ConnectionState == ConnectionState.Connected)
@ -142,7 +143,8 @@ namespace Kerobot.Services.Logging
{ {
Footer = new EmbedFooterBuilder() { Text = Name }, Footer = new EmbedFooterBuilder() { Text = Name },
Timestamp = DateTimeOffset.UtcNow, Timestamp = DateTimeOffset.UtcNow,
Description = "Error during recording to instance log.\nCheck the console.", Description = "Error during recording to instance log: `" +
insertException.Message + "`\nCheck the console.",
Color = Color.DarkRed Color = Color.DarkRed
}; };
await ch.SendMessageAsync("", embed: e.Build()); await ch.SendMessageAsync("", embed: e.Build());

View file

@ -38,7 +38,7 @@ namespace Kerobot.Modules.AutoResponder
public override Task<object> CreateGuildStateAsync(ulong guild, JToken config) public override Task<object> CreateGuildStateAsync(ulong guild, JToken config)
{ {
// Guild state is a read-only IEnumerable<Definition> // Guild state is a read-only IEnumerable<Definition>
if (config == null) return null; if (config == null) return Task.FromResult<object>(null);
var guildDefs = new List<Definition>(); var guildDefs = new List<Definition>();
foreach (var defconf in config.Children<JProperty>()) foreach (var defconf in config.Children<JProperty>())
{ {

View file

@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<Authors>Noikoio</Authors> <Authors>Noiiko</Authors>
<Company>Noikoio</Company>
<Product>Kerobot</Product> <Product>Kerobot</Product>
<Version>0.0.1</Version> <Version>0.0.1</Version>
<Description>Essential functions for Kerobot which are available in the public bot instance.</Description> <Description>Essential functions for Kerobot which are available in the public bot instance.</Description>

View file

@ -18,8 +18,6 @@ namespace Kerobot.Modules.RegexModerator
class ConfDefinition class ConfDefinition
{ {
public string Label { get; } public string Label { get; }
readonly RegexModerator _module; // TODO is this needed?
readonly ulong _guild; // corresponding guild, for debug purposes. (is this needed?)
// Matching settings // Matching settings
readonly IEnumerable<Regex> _regex; readonly IEnumerable<Regex> _regex;
@ -40,10 +38,8 @@ namespace Kerobot.Modules.RegexModerator
public bool RemovalSendUserNotification; // send ban/kick notification to user? public bool RemovalSendUserNotification; // send ban/kick notification to user?
public bool DeleteMessage { get; } public bool DeleteMessage { get; }
public ConfDefinition(RegexModerator instance, JObject def, ulong guildId) public ConfDefinition(JObject def)
{ {
_module = instance;
Label = def["Label"].Value<string>(); Label = def["Label"].Value<string>();
if (string.IsNullOrWhiteSpace(Label)) if (string.IsNullOrWhiteSpace(Label))
throw new ModuleLoadException("A rule does not have a label defined."); throw new ModuleLoadException("A rule does not have a label defined.");

View file

@ -23,8 +23,9 @@ namespace Kerobot.Modules.RegexModerator
if (config == null) return Task.FromResult<object>(null); if (config == null) return Task.FromResult<object>(null);
var defs = new List<ConfDefinition>(); var defs = new List<ConfDefinition>();
// TODO better error reporting during this process
foreach (var def in config.Children<JObject>()) foreach (var def in config.Children<JObject>())
defs.Add(new ConfDefinition(this, def, guildID)); defs.Add(new ConfDefinition(def));
if (defs.Count == 0) return Task.FromResult<object>(null); if (defs.Count == 0) return Task.FromResult<object>(null);
return Task.FromResult<object>(defs.AsReadOnly()); return Task.FromResult<object>(defs.AsReadOnly());
@ -49,19 +50,23 @@ namespace Kerobot.Modules.RegexModerator
// Send further processing to thread pool. // Send further processing to thread pool.
// Match checking is a CPU-intensive task, thus very little checking is done here. // Match checking is a CPU-intensive task, thus very little checking is done here.
var msgProcessingTasks = new List<Task>();
foreach (var item in defs) foreach (var item in defs)
{ {
// Need to check sender's moderator status here. Definition can't access mod list. // Need to check sender's moderator status here. Definition can't access mod list.
var isMod = GetModerators(ch.Guild.Id).IsListMatch(msg, true); var isMod = GetModerators(ch.Guild.Id).IsListMatch(msg, true);
var match = item.IsMatch(msg, isMod); var match = item.IsMatch(msg, isMod);
await Task.Run(async () => await ProcessMessage(item, msg, isMod)); msgProcessingTasks.Add(Task.Run(async () => await ProcessMessage(item, msg, isMod)));
} }
await Task.WhenAll(msgProcessingTasks);
} }
/// <summary> /// <summary>
/// Does further message checking and response execution. /// Does further message checking and response execution.
/// Invocations of this method are meant to be on the thread pool. /// Invocations of this method are meant to be placed onto a thread separate from the caller.
/// </summary> /// </summary>
private async Task ProcessMessage(ConfDefinition def, SocketMessage msg, bool isMod) private async Task ProcessMessage(ConfDefinition def, SocketMessage msg, bool isMod)
{ {

View file

@ -37,10 +37,10 @@ namespace Kerobot.Modules.AutoScriptResponder
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }
public override Task<object> CreateGuildStateAsync(JToken config) public override Task<object> CreateGuildStateAsync(ulong guild, JToken config)
{ {
// Guild state is a read-only IEnumerable<Definition> // Guild state is a read-only IEnumerable<Definition>
if (config == null) return null; if (config == null) return Task.FromResult<object>(null);
var guildDefs = new List<Definition>(); var guildDefs = new List<Definition>();
foreach (var defconf in config.Children<JProperty>()) foreach (var defconf in config.Children<JProperty>())
{ {

View file

@ -43,7 +43,7 @@ namespace Kerobot.Modules.EntryRole
public override Task<object> CreateGuildStateAsync(ulong guildID, JToken config) public override Task<object> CreateGuildStateAsync(ulong guildID, JToken config)
{ {
if (config == null) return null; if (config == null) return Task.FromResult<object>(null);
if (config.Type != JTokenType.Object) if (config.Type != JTokenType.Object)
throw new ModuleLoadException("Configuration is not properly defined."); throw new ModuleLoadException("Configuration is not properly defined.");

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<Authors>Noikoio</Authors> <Authors>Noiiko</Authors>
<Product>Kerobot</Product> <Product>Kerobot</Product>
<Version>0.0.1</Version> <Version>0.0.1</Version>
<Description>Kerobot modules with more specific purposes that may not work well in a public instance, but are manageable in self-hosted bot instances.</Description> <Description>Kerobot modules with more specific purposes that may not work well in a public instance, but are manageable in self-hosted bot instances.</Description>