Abandoning old plans, project will continue as RegexBot 3.0
This commit is contained in:
parent
6b83735d6b
commit
a7de9132ed
44 changed files with 608 additions and 339 deletions
219
.editorconfig
Normal file
219
.editorconfig
Normal file
|
@ -0,0 +1,219 @@
|
|||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# New line preferences
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = true
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = true
|
||||
csharp_style_expression_bodied_methods = true
|
||||
csharp_style_expression_bodied_operators = true
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = when_multiline
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_style_namespace_declarations = file_scoped
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_pattern_local_over_anonymous_function = true
|
||||
csharp_style_prefer_index_operator = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
|
||||
# New line preferences
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = false
|
||||
csharp_new_line_before_else = false
|
||||
csharp_new_line_before_finally = false
|
||||
csharp_new_line_before_members_in_anonymous_types = false
|
||||
csharp_new_line_before_members_in_object_initializers = false
|
||||
csharp_new_line_before_open_brace = none
|
||||
csharp_new_line_between_query_expression_clauses = false
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = flush_left
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
37
Kerobot.sln
37
Kerobot.sln
|
@ -1,37 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27428.2005
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kerobot", "Kerobot\Kerobot.csproj", "{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules-PublicInstance", "Modules-PublicInstance\Modules-PublicInstance.csproj", "{E06B76BE-E6F5-4803-BA02-DA8AF2A9705E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules-SelfHosted", "Modules-SelfHosted\Modules-SelfHosted.csproj", "{84390AD6-6906-4BD9-A948-578C21037D6C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E06B76BE-E6F5-4803-BA02-DA8AF2A9705E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E06B76BE-E6F5-4803-BA02-DA8AF2A9705E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E06B76BE-E6F5-4803-BA02-DA8AF2A9705E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E06B76BE-E6F5-4803-BA02-DA8AF2A9705E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{84390AD6-6906-4BD9-A948-578C21037D6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{84390AD6-6906-4BD9-A948-578C21037D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{84390AD6-6906-4BD9-A948-578C21037D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{84390AD6-6906-4BD9-A948-578C21037D6C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C823EC9C-CF20-4437-8B99-72158A6DD113}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,72 +0,0 @@
|
|||
using Discord.Net;
|
||||
|
||||
namespace Kerobot
|
||||
{
|
||||
// Instances of this are created by CommonFunctionService and by ModuleBase on behalf of CommonFunctionService,
|
||||
// and are meant to be sent to modules. This class has therefore been put within the Kerobot namespace.
|
||||
|
||||
/// <summary>
|
||||
/// Contains information on various success/failure outcomes for a ban or kick operation.
|
||||
/// </summary>
|
||||
public class BanKickResult
|
||||
{
|
||||
private readonly bool _userNotFound;
|
||||
|
||||
internal BanKickResult(HttpException error, bool notificationSuccess, bool errorNotFound)
|
||||
{
|
||||
OperationError = error;
|
||||
MessageSendSuccess = notificationSuccess;
|
||||
_userNotFound = errorNotFound;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the kick or ban succeeded.
|
||||
/// </summary>
|
||||
public bool OperationSuccess {
|
||||
get {
|
||||
if (ErrorNotFound) return false;
|
||||
if (OperationError == null) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception thrown, if any, when attempting to kick or ban the target.
|
||||
/// </summary>
|
||||
public HttpException OperationError { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the operation failed due to being unable to find the user.
|
||||
/// </summary>
|
||||
public bool ErrorNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_userNotFound) return true; // TODO I don't like this.
|
||||
if (OperationSuccess) return false;
|
||||
return OperationError.HttpCode == System.Net.HttpStatusCode.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the operation failed due to a permissions issue.
|
||||
/// </summary>
|
||||
public bool ErrorForbidden
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OperationSuccess) return false;
|
||||
return OperationError.HttpCode == System.Net.HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the user was able to receive the ban or kick message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="false"/> if an error was encountered when attempting to send the target a DM. Will always
|
||||
/// return <see langword="true"/> otherwise, including cases in which no message was sent.
|
||||
/// </value>
|
||||
public bool MessageSendSuccess { get; }
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Authors>Noiiko</Authors>
|
||||
<Product>Kerobot</Product>
|
||||
<Version>0.0.1</Version>
|
||||
<Description>Essential functions for Kerobot which are available in the public bot instance.</Description>
|
||||
<RootNamespace>Kerobot.Modules</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /Y "$(TargetDir)$(ProjectName).*" "$(SolutionDir)Kerobot\bin\$(ConfigurationName)\netcoreapp2.0\"" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Kerobot\Kerobot.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Authors>Noiiko</Authors>
|
||||
<Product>Kerobot</Product>
|
||||
<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>
|
||||
<RootNamespace>Kerobot.Modules</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /Y "$(TargetDir)$(ProjectName).*" "$(SolutionDir)Kerobot\bin\$(ConfigurationName)\netcoreapp2.0\"" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Kerobot\Kerobot.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
13
README.md
13
README.md
|
@ -1,9 +1,14 @@
|
|||
# Kerobot
|
||||
**This project is a major work in progress and is unusable in its current state. Not all features may be present.**
|
||||
# RegexBot
|
||||
**This branch contains code that is still a major work in progress, and is unusable in its current state. See the master branch for the current working version.**
|
||||
|
||||
Kerobot is a Discord moderation bot that takes some inspiration from Reddit's Automoderator. It provides a number of features to assist in watching over a busy server. Its configuration allows for a very high level of flexibility, ensuring that the bot behaves in accordance to the exact needs of your server.
|
||||
RegexBot is a self-hosted Discord moderation bot that takes some inspiration from Reddit's Automoderator. It provides a number of features to assist in watching over the tedious details in a busy server with no hidden details, arbitrary restrictions, or unmodifiable behavior. Its configuration allows for a very high level of flexibility, ensuring that the bot behaves in accordance to the exact needs of your server.
|
||||
|
||||
### Feature overview
|
||||
### Feature overview for 3.0:
|
||||
* Modular structure allows for extra features to be written, further enhancing the bot's customizability wherever it may be deployed.
|
||||
* Versatile JSON-based configuration, support for separate servers.
|
||||
* High detail logging and record-keeping prevents gaps in moderation that might occur with large public bots.
|
||||
|
||||
### Feature overview for RegexBotModule 3.0:
|
||||
* Create rules based on regular expression patterns
|
||||
* Follow up with custom responses ranging from sending a DM to disciplinary action
|
||||
* Create pattern-based triggers to provide information and fun to your users
|
||||
|
|
|
@ -3,17 +3,17 @@ using Newtonsoft.Json.Linq;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Modules.AutoResponder
|
||||
namespace RegexBot.Modules.AutoResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the capability to define text responses to pattern-based triggers for fun or informational
|
||||
/// purposes. Although in essence similar to <see cref="RegexModerator.RegexModerator"/>, it is a better
|
||||
/// fit for non-moderation use cases and has specific features suitable to that end.
|
||||
/// </summary>
|
||||
[KerobotModule]
|
||||
[RegexbotModule]
|
||||
class AutoResponder : ModuleBase
|
||||
{
|
||||
public AutoResponder(Kerobot kb) : base(kb)
|
||||
public AutoResponder(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
DiscordClient.MessageReceived += DiscordClient_MessageReceived;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Kerobot.Modules.AutoResponder
|
||||
namespace RegexBot.Modules.AutoResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of a single <see cref="AutoResponder"/> configuration definition.
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kerobot.Modules.AutoResponder
|
||||
namespace RegexBot.Modules.AutoResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for managing rate limit data.
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Modules.AutoScriptResponder
|
||||
namespace RegexBot.Modules.AutoScriptResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Meant to be highly identical to AutoResponder, save for its differentiating feature.
|
||||
|
@ -12,10 +12,10 @@ namespace Kerobot.Modules.AutoScriptResponder
|
|||
/// the other whenever they occur.
|
||||
/// The feature in question: It executes external scripts and replies with their output.
|
||||
/// </summary>
|
||||
[KerobotModule]
|
||||
[RegexbotModule]
|
||||
class AutoScriptResponder : ModuleBase
|
||||
{
|
||||
public AutoScriptResponder(Kerobot kb) : base(kb)
|
||||
public AutoScriptResponder(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
DiscordClient.MessageReceived += DiscordClient_MessageReceived;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Kerobot.Modules.AutoScriptResponder
|
||||
namespace RegexBot.Modules.AutoScriptResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of a single <see cref="AutoResponder"/> configuration definition.
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kerobot.Modules.AutoScriptResponder
|
||||
namespace RegexBot.Modules.AutoScriptResponder
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for managing rate limit data.
|
|
@ -7,19 +7,19 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Modules.EntryRole
|
||||
namespace RegexBot.Modules.EntryRole
|
||||
{
|
||||
/// <summary>
|
||||
/// Automatically sets a role onto users entering the guild.
|
||||
/// </summary>
|
||||
// TODO add persistent role support, make it an option
|
||||
[KerobotModule]
|
||||
[RegexbotModule]
|
||||
public class EntryRole : ModuleBase
|
||||
{
|
||||
readonly Task _workerTask;
|
||||
readonly CancellationTokenSource _workerTaskToken; // TODO make use of this when possible
|
||||
|
||||
public EntryRole(Kerobot kb) : base(kb)
|
||||
public EntryRole(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
DiscordClient.UserJoined += DiscordClient_UserJoined;
|
||||
DiscordClient.UserLeft += DiscordClient_UserLeft;
|
|
@ -1,9 +1,9 @@
|
|||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kerobot.Modules.EntryRole
|
||||
namespace RegexBot.Modules.EntryRole
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains configuration data as well as per-guild timers for those awaiting role assignment.
|
16
RegexBot-Modules/RegexBot-Modules.csproj
Normal file
16
RegexBot-Modules/RegexBot-Modules.csproj
Normal file
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>RegexBot.Modules</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Authors>NoiTheCat</Authors>
|
||||
<Description>A set of standard modules for use with RegexBot.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RegexBot\RegexBot.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,6 +1,6 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -8,13 +8,13 @@ using System.Diagnostics;
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Kerobot.Modules.RegexModerator
|
||||
namespace RegexBot.Modules.RegexModerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of a single RegexModerator rule for a guild.
|
||||
/// Data in this class is immutable. Contains various helper methods.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("RM rule '{_label}' for {_guild}")]
|
||||
[DebuggerDisplay("RM rule '{Label}'")]
|
||||
class ConfDefinition
|
||||
{
|
||||
public string Label { get; }
|
||||
|
@ -32,10 +32,11 @@ namespace Kerobot.Modules.RegexModerator
|
|||
public EntityName RoleRemove { get; }
|
||||
//readonly bool _rRolePersist; // TODO use when feature exists
|
||||
public EntityName ReportingChannel { get; }
|
||||
public Kerobot.RemovalType RemovalAction { get; } // ban, kick?
|
||||
public RegexBot.RemovalType RemoveAction { get; } // ban, kick?
|
||||
public int BanPurgeDays { get; }
|
||||
public string RemovalReason { get; } // reason to place into audit log, notification
|
||||
public bool RemovalSendUserNotification; // send ban/kick notification to user?
|
||||
public string RemoveReason { get; } // reason to place into audit log and notification
|
||||
public bool RemoveAnnounce { get; } // send success/failure message in invoking channel? default: true
|
||||
public bool RemoveNotifyTarget { get; } // send ban/kick notification to user?
|
||||
public bool DeleteMessage { get; }
|
||||
|
||||
public ConfDefinition(JObject def)
|
||||
|
@ -56,7 +57,7 @@ namespace Kerobot.Modules.RegexModerator
|
|||
|
||||
const string ErrNoRegex = "Regular expression patterns are not defined";
|
||||
var rxs = new List<Regex>();
|
||||
var rxconf = def["regex"];
|
||||
var rxconf = def["Regex"];
|
||||
if (rxconf == null) throw new ModuleLoadException(ErrNoRegex + errpostfx);
|
||||
if (rxconf.Type == JTokenType.Array)
|
||||
{
|
||||
|
@ -140,20 +141,19 @@ namespace Kerobot.Modules.RegexModerator
|
|||
}
|
||||
else ReportingChannel = null;
|
||||
|
||||
var removestr = resp[nameof(RemovalAction)]?.Value<string>();
|
||||
var removestr = resp[nameof(RemoveAction)]?.Value<string>();
|
||||
// accept values ban, kick, none
|
||||
switch (removestr)
|
||||
{
|
||||
case "ban": RemovalAction = Kerobot.RemovalType.Ban; break;
|
||||
case "kick": RemovalAction = Kerobot.RemovalType.Kick; break;
|
||||
case "none": RemovalAction = Kerobot.RemovalType.None; break;
|
||||
case "ban": RemoveAction = RegexBot.RemovalType.Ban; break;
|
||||
case "kick": RemoveAction = RegexBot.RemovalType.Kick; break;
|
||||
case "none": RemoveAction = RegexBot.RemovalType.None; break;
|
||||
default:
|
||||
if (removestr != null)
|
||||
throw new ModuleLoadException("RemoveAction is not set to a proper value" + errpostfx);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO extract BanPurgeDays
|
||||
int? banpurgeint;
|
||||
try { banpurgeint = resp[nameof(BanPurgeDays)]?.Value<int>(); }
|
||||
catch (InvalidCastException) { throw new ModuleLoadException("BanPurgeDays must be a numeric value."); }
|
||||
|
@ -164,9 +164,11 @@ namespace Kerobot.Modules.RegexModerator
|
|||
BanPurgeDays = banpurgeint ?? 0;
|
||||
}
|
||||
|
||||
RemovalReason = resp[nameof(RemovalReason)]?.Value<string>();
|
||||
RemoveReason = resp[nameof(RemoveReason)]?.Value<string>();
|
||||
|
||||
RemovalSendUserNotification = resp[nameof(RemovalSendUserNotification)]?.Value<bool>() ?? false;
|
||||
RemoveAnnounce = resp[nameof(RemoveAnnounce)]?.Value<bool>() ?? true;
|
||||
|
||||
RemoveNotifyTarget = resp[nameof(RemoveNotifyTarget)]?.Value<bool>() ?? false;
|
||||
|
||||
DeleteMessage = resp[nameof(DeleteMessage)]?.Value<bool>() ?? false;
|
||||
}
|
||||
|
@ -188,7 +190,7 @@ namespace Kerobot.Modules.RegexModerator
|
|||
// TODO enforce maximum execution time
|
||||
// TODO multi-processing of multiple regexes?
|
||||
// TODO metrics: temporary tracking of regex execution times
|
||||
if (regex.IsMatch(m.Content)) return true;
|
||||
if (regex.IsMatch(matchText)) return true;
|
||||
}
|
||||
|
||||
return false;
|
|
@ -3,16 +3,16 @@ using Newtonsoft.Json.Linq;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Modules.RegexModerator
|
||||
namespace RegexBot.Modules.RegexModerator
|
||||
{
|
||||
/// <summary>
|
||||
/// The 'star' feature of Kerobot. Users define pattern-based rules with other constraints.
|
||||
/// When triggered, each rule executes one or more different actions.
|
||||
/// </summary>
|
||||
[KerobotModule]
|
||||
[RegexbotModule]
|
||||
public class RegexModerator : ModuleBase
|
||||
{
|
||||
public RegexModerator(Kerobot kb) : base(kb)
|
||||
public RegexModerator(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
DiscordClient.MessageReceived += DiscordClient_MessageReceived;
|
||||
DiscordClient.MessageUpdated += DiscordClient_MessageUpdated;
|
||||
|
@ -23,6 +23,9 @@ namespace Kerobot.Modules.RegexModerator
|
|||
if (config == null) return Task.FromResult<object>(null);
|
||||
var defs = new List<ConfDefinition>();
|
||||
|
||||
if (config.Type != JTokenType.Array)
|
||||
throw new ModuleLoadException(this.Name + " configuration must be a JSON array.");
|
||||
|
||||
// TODO better error reporting during this process
|
||||
foreach (var def in config.Children<JObject>())
|
||||
defs.Add(new ConfDefinition(def));
|
||||
|
@ -41,6 +44,13 @@ namespace Kerobot.Modules.RegexModerator
|
|||
/// </summary>
|
||||
private async Task ReceiveIncomingMessage(SocketMessage msg)
|
||||
{
|
||||
if (msg.Author.Id == 0)
|
||||
{
|
||||
// TODO what changed to cause this? this wasn't happening before.
|
||||
System.Console.WriteLine($"Skip processing of message with empty metadata. Msg ID {msg.Id} - Msg content: {msg.Content} - Embed count: {msg.Embeds.Count}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore non-guild channels
|
||||
if (!(msg.Channel is SocketGuildChannel ch)) return;
|
||||
|
||||
|
@ -50,8 +60,6 @@ namespace Kerobot.Modules.RegexModerator
|
|||
|
||||
// Send further processing to thread pool.
|
||||
// Match checking is a CPU-intensive task, thus very little checking is done here.
|
||||
|
||||
|
||||
var msgProcessingTasks = new List<Task>();
|
||||
foreach (var item in defs)
|
||||
{
|
||||
|
@ -75,7 +83,7 @@ namespace Kerobot.Modules.RegexModerator
|
|||
|
||||
// TODO logging options for match result; handle here?
|
||||
|
||||
var executor = new ResponseExecutor(def, Kerobot);
|
||||
var executor = new ResponseExecutor(def, BotClient);
|
||||
await executor.Execute(msg);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static Kerobot.Kerobot;
|
||||
using static RegexBot.RegexbotClient;
|
||||
|
||||
namespace Kerobot.Modules.RegexModerator
|
||||
namespace RegexBot.Modules.RegexModerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to RegexModerator that executes the appropriate actions associated with a triggered rule.
|
||||
|
@ -15,13 +15,13 @@ namespace Kerobot.Modules.RegexModerator
|
|||
class ResponseExecutor
|
||||
{
|
||||
private readonly ConfDefinition _rule;
|
||||
private readonly Kerobot _bot;
|
||||
private readonly RegexbotClient _bot;
|
||||
private List<(ResponseAction, ResponseExecutionResult)> _results;
|
||||
|
||||
public ResponseExecutor(ConfDefinition rule, Kerobot kb)
|
||||
public ResponseExecutor(ConfDefinition rule, RegexbotClient bot)
|
||||
{
|
||||
_rule = rule;
|
||||
_bot = kb;
|
||||
_bot = bot;
|
||||
}
|
||||
|
||||
public async Task Execute(SocketMessage msg)
|
||||
|
@ -68,18 +68,33 @@ namespace Kerobot.Modules.RegexModerator
|
|||
|
||||
private Task<ResponseExecutionResult> DoBan(SocketGuild g, SocketMessage msg)
|
||||
{
|
||||
if (_rule.RemovalAction != RemovalType.Ban) return Task.FromResult<ResponseExecutionResult>(null);
|
||||
return DoBanOrKick(g, msg, _rule.RemovalAction);
|
||||
if (_rule.RemoveAction != RemovalType.Ban) return Task.FromResult<ResponseExecutionResult>(null);
|
||||
return DoBanOrKick(g, msg, _rule.RemoveAction);
|
||||
}
|
||||
private Task<ResponseExecutionResult> DoKick(SocketGuild g, SocketMessage msg)
|
||||
{
|
||||
if (_rule.RemovalAction != RemovalType.Kick) return Task.FromResult<ResponseExecutionResult>(null);
|
||||
return DoBanOrKick(g, msg, _rule.RemovalAction);
|
||||
if (_rule.RemoveAction != RemovalType.Kick) return Task.FromResult<ResponseExecutionResult>(null);
|
||||
return DoBanOrKick(g, msg, _rule.RemoveAction);
|
||||
}
|
||||
private async Task<ResponseExecutionResult> DoBanOrKick(SocketGuild g, SocketMessage msg, RemovalType t)
|
||||
{
|
||||
var result = await _bot.BanOrKickAsync(t, g, $"Rule '{_rule.Label}'",
|
||||
msg.Author.Id, _rule.BanPurgeDays, _rule.RemovalReason, _rule.RemovalSendUserNotification);
|
||||
msg.Author.Id, _rule.BanPurgeDays, _rule.RemoveReason, _rule.RemoveNotifyTarget);
|
||||
|
||||
string logAnnounce = null;
|
||||
if (_rule.RemoveAnnounce)
|
||||
{
|
||||
try
|
||||
{
|
||||
await msg.Channel.SendMessageAsync(result.GetResultString(_bot, g.Id));
|
||||
}
|
||||
catch (Discord.Net.HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
logAnnounce = "Could not send " + (t == RemovalType.Ban ? "ban" : "kick") + " announcement to channel "
|
||||
+ "due to a permissions issue.";
|
||||
}
|
||||
}
|
||||
|
||||
if (result.ErrorForbidden)
|
||||
{
|
||||
return new ResponseExecutionResult(false, ForbiddenGenericError);
|
||||
|
@ -88,10 +103,7 @@ namespace Kerobot.Modules.RegexModerator
|
|||
{
|
||||
return new ResponseExecutionResult(false, "The target user is no longer in the server.");
|
||||
}
|
||||
else return new ResponseExecutionResult(true, null);
|
||||
|
||||
// TODO option to announce ban/kick result in the trigger channel
|
||||
// ^ implementation: take result, reply to channel. don't alter BanOrKickAsync.
|
||||
else return new ResponseExecutionResult(true, logAnnounce);
|
||||
}
|
||||
|
||||
private async Task<ResponseExecutionResult> DoDelete(SocketGuild g, SocketMessage msg)
|
34
RegexBot.sln
Normal file
34
RegexBot.sln
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RegexBot", "RegexBot\RegexBot.csproj", "{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegexBot-Modules", "RegexBot-Modules\RegexBot-Modules.csproj", "{03111D82-30ED-4FDF-A512-87BDC05C6DA1}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3} = {6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FA3A92F-F1FC-4BA8-8018-1A05CB4C7FA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{03111D82-30ED-4FDF-A512-87BDC05C6DA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{03111D82-30ED-4FDF-A512-87BDC05C6DA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{03111D82-30ED-4FDF-A512-87BDC05C6DA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{03111D82-30ED-4FDF-A512-87BDC05C6DA1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C823EC9C-CF20-4437-8B99-72158A6DD113}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -5,7 +5,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Kerobot.Common
|
||||
namespace RegexBot.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a commonly-used configuration structure: an array of strings consisting of <see cref="EntityName"/> values.
|
|
@ -4,7 +4,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Kerobot.Common
|
||||
namespace RegexBot.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of entity specified in an <see cref="EntityName"/>.
|
|
@ -2,7 +2,7 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace Kerobot.Common
|
||||
namespace RegexBot.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents commonly-used configuration regarding whitelist/blacklist filtering, including exemptions.
|
|
@ -1,12 +1,12 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RegexBot;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Kerobot
|
||||
{
|
||||
namespace RegexBot {
|
||||
/// <summary>
|
||||
/// Contains instance configuration for this bot,
|
||||
/// including Discord connection settings and service configuration.
|
|
@ -1,12 +1,12 @@
|
|||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using static Kerobot.Kerobot;
|
||||
using static RegexBot.RegexbotClient;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for a Kerobot module. A module implements a user-facing feature and is expected to directly handle
|
||||
|
@ -21,18 +21,18 @@ namespace Kerobot
|
|||
/// <summary>
|
||||
/// Retrieves the Kerobot instance.
|
||||
/// </summary>
|
||||
public Kerobot Kerobot { get; }
|
||||
public RegexbotClient BotClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Discord client instance.
|
||||
/// </summary>
|
||||
public DiscordSocketClient DiscordClient { get => Kerobot.DiscordClient; }
|
||||
public DiscordSocketClient DiscordClient { get => BotClient.DiscordClient; }
|
||||
|
||||
/// <summary>
|
||||
/// When a module is loaded, this constructor is called.
|
||||
/// Services are available at this point. Do not attempt to communicate to Discord within the constructor.
|
||||
/// </summary>
|
||||
public ModuleBase(Kerobot kb) => Kerobot = kb;
|
||||
public ModuleBase(RegexbotClient bot) => BotClient = bot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module name.
|
||||
|
@ -61,7 +61,7 @@ namespace Kerobot
|
|||
/// Thrown if the stored state object cannot be cast as specified.
|
||||
/// </exception>
|
||||
[DebuggerStepThrough]
|
||||
protected T GetGuildState<T>(ulong guildId) => Kerobot.GetGuildState<T>(guildId, GetType());
|
||||
protected T GetGuildState<T>(ulong guildId) => BotClient.GetGuildState<T>(guildId, GetType());
|
||||
|
||||
/// <summary>
|
||||
/// Appends a message to the global instance log. Use sparingly.
|
||||
|
@ -70,12 +70,12 @@ namespace Kerobot
|
|||
/// Specifies if the log message should be sent to the reporting channel.
|
||||
/// Only messages of very high importance should use this option.
|
||||
/// </param>
|
||||
protected Task LogAsync(string message, bool report = false) => Kerobot.InstanceLogAsync(report, Name, message);
|
||||
protected Task LogAsync(string message, bool report = false) => BotClient.InstanceLogAsync(report, Name, message);
|
||||
|
||||
/// <summary>
|
||||
/// Appends a message to the log for the specified guild.
|
||||
/// </summary>
|
||||
protected Task LogAsync(ulong guild, string message) => Kerobot.GuildLogAsync(guild, Name, message);
|
||||
protected Task LogAsync(ulong guild, string message) => BotClient.GuildLogAsync(guild, Name, message);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to ban the given user from the specified guild. It is greatly preferred to call this method
|
||||
|
@ -90,7 +90,7 @@ namespace Kerobot
|
|||
/// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param>
|
||||
/// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action being taken.</param>
|
||||
protected Task<BanKickResult> BanAsync(SocketGuild guild, string source, ulong targetUser, int purgeDays, string reason, bool sendDMToTarget)
|
||||
=> Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, purgeDays, reason, sendDMToTarget);
|
||||
=> BotClient.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, purgeDays, reason, sendDMToTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="BanAsync(SocketGuild, string, ulong, int, string, bool)"/>, but making use of an
|
||||
|
@ -99,8 +99,8 @@ namespace Kerobot
|
|||
/// <param name="targetSearch">The EntityCache search string.</param>
|
||||
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);
|
||||
if (result == null) return new BanKickResult(null, false, true);
|
||||
var result = await BotClient.EcQueryUser(guild.Id, targetSearch);
|
||||
if (result == null) return new BanKickResult(null, false, true, RemovalType.Ban, 0);
|
||||
return await BanAsync(guild, source, result.UserID, purgeDays, reason, sendDMToTarget);
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ namespace Kerobot
|
|||
/// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param>
|
||||
/// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action being taken.</param>
|
||||
protected Task<BanKickResult> KickAsync(SocketGuild guild, string source, ulong targetUser, string reason, bool sendDMToTarget)
|
||||
=> Kerobot.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, 0, reason, sendDMToTarget);
|
||||
=> BotClient.BanOrKickAsync(RemovalType.Ban, guild, source, targetUser, 0, reason, sendDMToTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="KickAsync(SocketGuild, string, ulong, string, bool)"/>, but making use of an
|
||||
|
@ -125,8 +125,8 @@ namespace Kerobot
|
|||
/// <param name="targetSearch">The EntityCache search string.</param>
|
||||
protected async Task<BanKickResult> KickAsync(SocketGuild guild, string source, string targetSearch, string reason, bool sendDMToTarget)
|
||||
{
|
||||
var result = await Kerobot.EcQueryUser(guild.Id, targetSearch);
|
||||
if (result == null) return new BanKickResult(null, false, true);
|
||||
var result = await BotClient.EcQueryUser(guild.Id, targetSearch);
|
||||
if (result == null) return new BanKickResult(null, false, true, RemovalType.Kick, 0);
|
||||
return await KickAsync(guild, source, result.UserID, reason, sendDMToTarget);
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ namespace Kerobot
|
|||
/// An <see cref="EntityList"/> with corresponding moderator configuration data.
|
||||
/// In case none exists, an empty list will be returned.
|
||||
/// </returns>
|
||||
protected EntityList GetModerators(ulong guild) => Kerobot.GetModerators(guild);
|
||||
protected EntityList GetModerators(ulong guild) => BotClient.GetModerators(guild);
|
||||
}
|
||||
|
||||
/// <summary>
|
|
@ -5,7 +5,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
static class ModuleLoader
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace Kerobot
|
|||
/// <summary>
|
||||
/// Given the instance configuration, loads all appropriate types from file specified in it.
|
||||
/// </summary>
|
||||
internal static ReadOnlyCollection<ModuleBase> Load(InstanceConfig conf, Kerobot k)
|
||||
internal static ReadOnlyCollection<ModuleBase> Load(InstanceConfig conf, RegexbotClient k)
|
||||
{
|
||||
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
var modules = new List<ModuleBase>();
|
||||
|
@ -50,11 +50,11 @@ namespace Kerobot
|
|||
return modules.AsReadOnly();
|
||||
}
|
||||
|
||||
static IEnumerable<ModuleBase> LoadModulesFromAssembly(Assembly asm, Kerobot k)
|
||||
static IEnumerable<ModuleBase> LoadModulesFromAssembly(Assembly asm, RegexbotClient k)
|
||||
{
|
||||
var eligibleTypes = from type in asm.GetTypes()
|
||||
where !type.IsAssignableFrom(typeof(ModuleBase))
|
||||
where type.GetCustomAttribute<KerobotModuleAttribute>() != null
|
||||
where type.GetCustomAttribute<RegexbotModuleAttribute>() != null
|
||||
select type;
|
||||
k.InstanceLogAsync(false, LogName, $"Scanning {asm.GetName().Name}");
|
||||
|
|
@ -2,13 +2,11 @@
|
|||
using CommandLine.Text;
|
||||
using System;
|
||||
|
||||
namespace Kerobot
|
||||
{
|
||||
namespace RegexBot {
|
||||
/// <summary>
|
||||
/// Command line options
|
||||
/// </summary>
|
||||
class Options
|
||||
{
|
||||
class Options {
|
||||
[Option('c', "config", Default = null,
|
||||
HelpText = "Custom path to instance configuration. Defaults to config.json in bot directory.")]
|
||||
public string ConfigFile { get; set; }
|
||||
|
@ -16,19 +14,17 @@ namespace Kerobot
|
|||
/// <summary>
|
||||
/// Command line arguments parsed here. Depending on inputs, the program can exit here.
|
||||
/// </summary>
|
||||
public static Options ParseOptions(string[] args)
|
||||
{
|
||||
public static Options ParseOptions(string[] args) {
|
||||
// Parser will not write out to console by itself
|
||||
var parser = new Parser(config => config.HelpWriter = null);
|
||||
Options opts = null;
|
||||
|
||||
var result = parser.ParseArguments<Options>(args);
|
||||
result.WithParsed(p => opts = p);
|
||||
result.WithNotParsed(p =>
|
||||
{
|
||||
result.WithNotParsed(p => {
|
||||
// Taking some extra steps to modify the header to make it resemble our welcome message.
|
||||
var ht = HelpText.AutoBuild(result);
|
||||
ht.Heading = ht.Heading += " - https://github.com/Noikoio/Kerobot";
|
||||
ht.Heading += " - https://github.com/NoiTheCat/RegexBot";
|
||||
Console.WriteLine(ht.ToString());
|
||||
Environment.Exit(1);
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using RegexBot;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot
|
||||
{
|
||||
namespace RegexBot {
|
||||
/// <summary>
|
||||
/// Program startup class.
|
||||
/// </summary>
|
||||
|
@ -15,7 +15,7 @@ namespace Kerobot
|
|||
/// </summary>
|
||||
public static DateTimeOffset StartTime { get; private set; }
|
||||
|
||||
static Kerobot _main;
|
||||
static RegexbotClient _main;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point. Loads options, initializes all components, then connects to Discord.
|
||||
|
@ -64,7 +64,7 @@ namespace Kerobot
|
|||
});
|
||||
|
||||
// Kerobot class initialization - will set up services and modules
|
||||
_main = new Kerobot(cfg, client);
|
||||
_main = new RegexbotClient(cfg, client);
|
||||
|
||||
// Set up application close handler
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
|
@ -82,7 +82,7 @@ namespace Kerobot
|
|||
{
|
||||
e.Cancel = true;
|
||||
|
||||
_main.InstanceLogAsync(true, "Kerobot", "Shutting down. Reason: Interrupt signal.");
|
||||
_main.InstanceLogAsync(true, nameof(RegexBot), "Shutting down. Reason: Interrupt signal.");
|
||||
|
||||
// 5 seconds of leeway - any currently running tasks will need time to finish executing
|
||||
var leeway = Task.Delay(5000);
|
||||
|
@ -93,7 +93,7 @@ namespace Kerobot
|
|||
leeway.Wait();
|
||||
|
||||
bool success = _main.DiscordClient.StopAsync().Wait(1000);
|
||||
if (!success) _main.InstanceLogAsync(false, "Kerobot",
|
||||
if (!success) _main.InstanceLogAsync(false, nameof(RegexBot),
|
||||
"Failed to disconnect cleanly from Discord. Will force shut down.").Wait();
|
||||
Environment.Exit(0);
|
||||
}
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<StartupObject>Kerobot.Program</StartupObject>
|
||||
<Authors>Noiiko</Authors>
|
||||
<Company />
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Authors>NoiTheCat</Authors>
|
||||
<Description>Advanced and flexible Discord moderation bot.</Description>
|
||||
<Version>0.0.1</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -25,10 +24,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.5.0" />
|
||||
<PackageReference Include="Discord.Net" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.7" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.4.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,16 +1,16 @@
|
|||
using Discord.WebSocket;
|
||||
using Kerobot.Services;
|
||||
using RegexBot.Services;
|
||||
using Npgsql;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
/// <summary>
|
||||
/// Kerobot main class, and the most accessible and useful class in the whole program.
|
||||
/// Provides an interface for any part of the program to call into all existing services.
|
||||
/// </summary>
|
||||
public partial class Kerobot
|
||||
public partial class RegexbotClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets application instance configuration.
|
||||
|
@ -32,7 +32,7 @@ namespace Kerobot
|
|||
/// </summary>
|
||||
internal IReadOnlyCollection<ModuleBase> Modules { get; }
|
||||
|
||||
internal Kerobot(InstanceConfig conf, DiscordSocketClient client)
|
||||
internal RegexbotClient(InstanceConfig conf, DiscordSocketClient client)
|
||||
{
|
||||
Config = conf;
|
||||
DiscordClient = client;
|
||||
|
@ -45,8 +45,8 @@ namespace Kerobot
|
|||
|
||||
// Everything's ready to go. Print the welcome message here.
|
||||
var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
InstanceLogAsync(false, "Kerobot",
|
||||
$"This is Kerobot v{ver.ToString(3)}. https://github.com/Noiiko/Kerobot").Wait();
|
||||
InstanceLogAsync(false, nameof(RegexBot),
|
||||
$"This is RegexBot v{ver.ToString(3)}. https://github.com/NoiTheCat/RegexBot").Wait();
|
||||
|
||||
// We return to Program.cs at this point.
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies to the Kerobot module loader that the target class should be treated as a module instance.
|
||||
|
@ -8,5 +8,5 @@ namespace Kerobot
|
|||
/// the program searches for classes implementing <see cref="ModuleBase"/> that also contain this attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class KerobotModuleAttribute : Attribute { }
|
||||
public class RegexbotModuleAttribute : Attribute { }
|
||||
}
|
132
RegexBot/Services/CommonFunctions/BanKickResult.cs
Normal file
132
RegexBot/Services/CommonFunctions/BanKickResult.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
using Discord.Net;
|
||||
using static RegexBot.RegexbotClient;
|
||||
|
||||
namespace RegexBot
|
||||
{
|
||||
// Instances of this are created by CommonFunctionService and by ModuleBase on behalf of CommonFunctionService,
|
||||
// and are meant to be sent to modules. This class has therefore been put within the Kerobot namespace.
|
||||
|
||||
/// <summary>
|
||||
/// Contains information on various success/failure outcomes for a ban or kick operation.
|
||||
/// </summary>
|
||||
public class BanKickResult
|
||||
{
|
||||
private readonly bool _userNotFound; // possible to receive this error by means other than exception
|
||||
private readonly RemovalType _rptRemovalType;
|
||||
private readonly ulong _rptTargetId;
|
||||
|
||||
internal BanKickResult(HttpException error, bool notificationSuccess, bool errorNotFound,
|
||||
RemovalType rtype, ulong rtarget)
|
||||
{
|
||||
OperationError = error;
|
||||
MessageSendSuccess = notificationSuccess;
|
||||
_userNotFound = errorNotFound;
|
||||
_rptRemovalType = rtype;
|
||||
_rptTargetId = rtarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the kick or ban succeeded.
|
||||
/// </summary>
|
||||
public bool OperationSuccess {
|
||||
get {
|
||||
if (ErrorNotFound) return false;
|
||||
if (OperationError != null) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception thrown, if any, when attempting to kick or ban the target.
|
||||
/// </summary>
|
||||
public HttpException OperationError { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the operation failed due to being unable to find the user.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This may return true even if <see cref="OperationError"/> returns null.
|
||||
/// This type of error may appear in cases that do not involve an exception being thrown.
|
||||
/// </remarks>
|
||||
public bool ErrorNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_userNotFound) return true;
|
||||
if (OperationError != null) return OperationError.HttpCode == System.Net.HttpStatusCode.NotFound;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the operation failed due to a permissions issue.
|
||||
/// </summary>
|
||||
public bool ErrorForbidden
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OperationSuccess) return false;
|
||||
return OperationError.HttpCode == System.Net.HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the user was able to receive the ban or kick message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="false"/> if an error was encountered when attempting to send the target a DM. Will always
|
||||
/// return <see langword="true"/> otherwise, including cases in which no message was sent.
|
||||
/// </value>
|
||||
public bool MessageSendSuccess { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a message representative of the ban/kick result that may be posted as-is
|
||||
/// within the a Discord channel.
|
||||
/// </summary>
|
||||
public string GetResultString(RegexbotClient bot, ulong guildId)
|
||||
{
|
||||
string msg;
|
||||
|
||||
if (OperationSuccess) msg = ":white_check_mark: ";
|
||||
else msg = ":x: Failed to ";
|
||||
|
||||
if (_rptRemovalType == RemovalType.Ban)
|
||||
{
|
||||
if (OperationSuccess) msg += "Banned";
|
||||
else msg += "ban";
|
||||
}
|
||||
else if (_rptRemovalType == RemovalType.Kick)
|
||||
{
|
||||
if (OperationSuccess) msg += "Kicked";
|
||||
else msg += "kick";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.InvalidOperationException("Cannot create a message for removal type of None.");
|
||||
}
|
||||
|
||||
if (_rptTargetId != 0)
|
||||
{
|
||||
var user = bot.EcQueryUser(guildId, _rptTargetId.ToString()).GetAwaiter().GetResult();
|
||||
if (user != null)
|
||||
{
|
||||
// TODO sanitize possible formatting characters in display name
|
||||
msg += $" user **{user.Username}#{user.Discriminator}**";
|
||||
}
|
||||
}
|
||||
|
||||
if (OperationSuccess)
|
||||
{
|
||||
msg += ".";
|
||||
if (!MessageSendSuccess) msg += "\n(User was unable to receive notification message.)";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ErrorNotFound) msg += ": The specified user could not be found.";
|
||||
else if (ErrorForbidden) msg += ": I do not have the required permissions to perform that action.";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
using static Kerobot.Kerobot;
|
||||
using static RegexBot.RegexbotClient;
|
||||
|
||||
namespace Kerobot.Services.CommonFunctions
|
||||
namespace RegexBot.Services.CommonFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements certain common actions that modules may want to perform. Using this service to perform those
|
||||
|
@ -16,7 +16,7 @@ namespace Kerobot.Services.CommonFunctions
|
|||
/// </summary>
|
||||
internal class CommonFunctionsService : Service
|
||||
{
|
||||
public CommonFunctionsService(Kerobot kb) : base(kb) { }
|
||||
public CommonFunctionsService(RegexbotClient bot) : base(bot) { }
|
||||
|
||||
#region Guild member removal
|
||||
/// <summary>
|
||||
|
@ -33,13 +33,9 @@ namespace Kerobot.Services.CommonFunctions
|
|||
|
||||
SocketGuildUser utarget = guild.GetUser(target);
|
||||
// Can't kick without obtaining user object. Quit here.
|
||||
if (t == RemovalType.Kick && utarget == null) return new BanKickResult(null, false, true);
|
||||
if (t == RemovalType.Kick && utarget == null) return new BanKickResult(null, false, true, RemovalType.Kick, 0);
|
||||
|
||||
#if DEBUG
|
||||
#warning "Services are NOT NOTIFIED" of bans/kicks during debug."
|
||||
#else
|
||||
// TODO notify services here as soon as we get some who will want to listen to this
|
||||
#endif
|
||||
|
||||
// Send DM notification
|
||||
if (sendDmToTarget)
|
||||
|
@ -51,23 +47,19 @@ namespace Kerobot.Services.CommonFunctions
|
|||
// Perform the action
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
#warning "Actual kick/ban is DISABLED during debug."
|
||||
#else
|
||||
if (t == RemovalType.Ban) await guild.AddBanAsync(target, banPurgeDays);
|
||||
else await utarget.KickAsync(logReason);
|
||||
// TODO !! Insert ban reason! For kick also: Figure out a way to specify invoker.
|
||||
#endif
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
return new BanKickResult(ex, dmSuccess, false);
|
||||
return new BanKickResult(ex, dmSuccess, false, t, target);
|
||||
}
|
||||
|
||||
return new BanKickResult(null, dmSuccess, false);
|
||||
return new BanKickResult(null, dmSuccess, false, t, target);
|
||||
}
|
||||
|
||||
private async Task<bool> BanKickSendNotificationAsync(SocketGuildUser target, Kerobot.RemovalType action, string reason)
|
||||
private async Task<bool> BanKickSendNotificationAsync(SocketGuildUser target, RemovalType action, string reason)
|
||||
{
|
||||
const string DMTemplate = "You have been {0} from {1} for the following reason:\n{2}";
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
using Discord.WebSocket;
|
||||
using System.Threading.Tasks;
|
||||
using Kerobot.Services.CommonFunctions;
|
||||
using RegexBot.Services.CommonFunctions;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
partial class Kerobot
|
||||
partial class RegexbotClient
|
||||
{
|
||||
private CommonFunctionsService _svcCommonFunctions;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace Kerobot // Publicly accessible class; placing in main namespace
|
||||
namespace RegexBot // Publicly accessible class; placing in main namespace
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of user information retrieved from Kerobot's UserCache.
|
|
@ -1,6 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Services.EntityCache
|
||||
namespace RegexBot.Services.EntityCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides and maintains a database-backed cache of entities. Portions of information collected by this
|
||||
|
@ -11,14 +11,14 @@ namespace Kerobot.Services.EntityCache
|
|||
{
|
||||
private readonly UserCache _uc;
|
||||
|
||||
internal EntityCacheService(Kerobot kb) : base(kb)
|
||||
internal EntityCacheService(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
// Currently we only have UserCache. May add Channel and Server caches later.
|
||||
_uc = new UserCache(kb);
|
||||
_uc = new UserCache(bot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Kerobot.EcQueryUser(ulong, string)"/>.
|
||||
/// See <see cref="RegexbotClient.EcQueryUser(ulong, string)"/>.
|
||||
/// </summary>
|
||||
internal Task<CachedUser> QueryUserCache(ulong guildId, string search) => _uc.Query(guildId, search);
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
using Kerobot.Services.EntityCache;
|
||||
using RegexBot.Services.EntityCache;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
partial class Kerobot
|
||||
partial class RegexbotClient
|
||||
{
|
||||
private EntityCacheService _svcEntityCache;
|
||||
|
|
@ -4,7 +4,7 @@ using System;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Services.EntityCache
|
||||
namespace RegexBot.Services.EntityCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides and maintains a database-backed cache of users.
|
||||
|
@ -13,15 +13,15 @@ namespace Kerobot.Services.EntityCache
|
|||
/// </summary>
|
||||
class UserCache
|
||||
{
|
||||
private Kerobot _kb;
|
||||
private RegexbotClient _bot;
|
||||
|
||||
internal UserCache(Kerobot kb)
|
||||
internal UserCache(RegexbotClient bot)
|
||||
{
|
||||
_kb = kb;
|
||||
_bot = bot;
|
||||
CreateDatabaseTablesAsync().Wait();
|
||||
|
||||
kb.DiscordClient.GuildMemberUpdated += DiscordClient_GuildMemberUpdated;
|
||||
kb.DiscordClient.UserUpdated += DiscordClient_UserUpdated;
|
||||
bot.DiscordClient.GuildMemberUpdated += DiscordClient_GuildMemberUpdated;
|
||||
bot.DiscordClient.UserUpdated += DiscordClient_UserUpdated;
|
||||
}
|
||||
|
||||
#region Database setup
|
||||
|
@ -31,7 +31,7 @@ namespace Kerobot.Services.EntityCache
|
|||
|
||||
private async Task CreateDatabaseTablesAsync()
|
||||
{
|
||||
using (var db = await _kb.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await _bot.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ namespace Kerobot.Services.EntityCache
|
|||
#region Database updates
|
||||
private async Task DiscordClient_UserUpdated(SocketUser old, SocketUser current)
|
||||
{
|
||||
using (var db = await _kb.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await _bot.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -109,7 +109,7 @@ namespace Kerobot.Services.EntityCache
|
|||
// 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 _bot.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -135,7 +135,7 @@ namespace Kerobot.Services.EntityCache
|
|||
private static readonly Regex DiscriminatorSearch = new Regex(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Kerobot.EcQueryUser(ulong, string)"/>.
|
||||
/// See <see cref="RegexbotClient.EcQueryUser(ulong, string)"/>.
|
||||
/// </summary>
|
||||
internal async Task<CachedUser> Query(ulong guildID, string search)
|
||||
{
|
||||
|
@ -171,7 +171,7 @@ namespace Kerobot.Services.EntityCache
|
|||
|
||||
private async Task<CachedUser> InternalDoQuery(ulong guildId, ulong? sID, string sName, string sDisc)
|
||||
{
|
||||
using (var db = await _kb.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await _bot.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
var c = db.CreateCommand();
|
||||
c.CommandText = $"select * from {UserView} " +
|
|
@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
|||
using Discord;
|
||||
using NpgsqlTypes;
|
||||
|
||||
namespace Kerobot.Services.EventLogging
|
||||
namespace RegexBot.Services.EventLogging
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements logging. Logging is distinguished into two types: Instance and per-guild.
|
||||
|
@ -16,17 +16,17 @@ namespace Kerobot.Services.EventLogging
|
|||
// Note: Service.Log's functionality is implemented here. Don't use it within this class.
|
||||
// If necessary, use DoInstanceLogAsync instead.
|
||||
|
||||
internal EventLoggingService(Kerobot kb) : base(kb)
|
||||
internal EventLoggingService(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
// Create logging table
|
||||
CreateDatabaseTablesAsync().Wait();
|
||||
|
||||
// Discord.Net log handling (client logging option is specified in Program.cs)
|
||||
kb.DiscordClient.Log += DiscordClient_Log;
|
||||
bot.DiscordClient.Log += DiscordClient_Log;
|
||||
|
||||
// Ready message too
|
||||
kb.DiscordClient.Ready +=
|
||||
async delegate { await DoInstanceLogAsync(true, "Kerobot", "Connected and ready."); };
|
||||
bot.DiscordClient.Ready +=
|
||||
async delegate { await DoInstanceLogAsync(true, nameof(RegexBot), "Connected and ready."); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -48,7 +48,7 @@ namespace Kerobot.Services.EventLogging
|
|||
const string TableLog = "program_log";
|
||||
private async Task CreateDatabaseTablesAsync()
|
||||
{
|
||||
using (var db = await Kerobot.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await BotClient.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ namespace Kerobot.Services.EventLogging
|
|||
}
|
||||
private async Task TableInsertAsync(ulong guildId, DateTimeOffset timestamp, string source, string message)
|
||||
{
|
||||
using (var db = await Kerobot.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await BotClient.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -104,7 +104,7 @@ namespace Kerobot.Services.EventLogging
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Kerobot.InstanceLogAsync(bool, string, string)"/>
|
||||
/// See <see cref="RegexbotClient.InstanceLogAsync(bool, string, string)"/>
|
||||
/// </summary>
|
||||
public async Task DoInstanceLogAsync(bool report, string source, string message)
|
||||
{
|
||||
|
@ -127,11 +127,11 @@ namespace Kerobot.Services.EventLogging
|
|||
|
||||
// Report to logging channel if necessary and possible
|
||||
// TODO replace with webhook?
|
||||
var (g, c) = Kerobot.Config.InstanceLogReportTarget;
|
||||
var (g, c) = BotClient.Config.InstanceLogReportTarget;
|
||||
if ((insertException != null || report) &&
|
||||
g != 0 && c != 0 && Kerobot.DiscordClient.ConnectionState == ConnectionState.Connected)
|
||||
g != 0 && c != 0 && BotClient.DiscordClient.ConnectionState == ConnectionState.Connected)
|
||||
{
|
||||
var ch = Kerobot.DiscordClient.GetGuild(g)?.GetTextChannel(c);
|
||||
var ch = BotClient.DiscordClient.GetGuild(g)?.GetTextChannel(c);
|
||||
if (ch == null) return; // not connected, or channel doesn't exist.
|
||||
|
||||
if (insertException != null)
|
||||
|
@ -176,13 +176,16 @@ namespace Kerobot.Services.EventLogging
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Kerobot.GuildLogAsync(ulong, string, string)"/>
|
||||
/// See <see cref="RegexbotClient.GuildLogAsync(ulong, string, string)"/>
|
||||
/// </summary>
|
||||
public async Task DoGuildLogAsync(ulong guild, string source, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
await TableInsertAsync(guild, DateTimeOffset.UtcNow, source, message);
|
||||
#if DEBUG
|
||||
FormatToConsole(DateTimeOffset.UtcNow, $"DEBUG {guild} - {source}", message);
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
|
@ -1,9 +1,9 @@
|
|||
using Kerobot.Services.EventLogging;
|
||||
using RegexBot.Services.EventLogging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
partial class Kerobot
|
||||
partial class RegexbotClient
|
||||
{
|
||||
EventLoggingService _svcLogging;
|
||||
|
|
@ -4,11 +4,11 @@ using System.IO;
|
|||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using Kerobot.Common;
|
||||
using RegexBot.Common;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Kerobot.Services.GuildState
|
||||
namespace RegexBot.Services.GuildState
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements per-module storage and retrieval of guild-specific state data.
|
||||
|
@ -22,15 +22,15 @@ namespace Kerobot.Services.GuildState
|
|||
|
||||
const string GuildLogSource = "Configuration loader";
|
||||
|
||||
public GuildStateService(Kerobot kb) : base(kb)
|
||||
public GuildStateService(RegexbotClient bot) : base(bot)
|
||||
{
|
||||
_moderators = new Dictionary<ulong, EntityList>();
|
||||
_states = new Dictionary<ulong, Dictionary<Type, StateInfo>>();
|
||||
CreateDatabaseTablesAsync().Wait();
|
||||
|
||||
kb.DiscordClient.GuildAvailable += DiscordClient_GuildAvailable;
|
||||
kb.DiscordClient.JoinedGuild += DiscordClient_JoinedGuild;
|
||||
kb.DiscordClient.LeftGuild += DiscordClient_LeftGuild;
|
||||
bot.DiscordClient.GuildAvailable += DiscordClient_GuildAvailable;
|
||||
bot.DiscordClient.JoinedGuild += DiscordClient_JoinedGuild;
|
||||
bot.DiscordClient.LeftGuild += DiscordClient_LeftGuild;
|
||||
|
||||
// TODO periodic task for refreshing stale configuration
|
||||
}
|
||||
|
@ -57,12 +57,12 @@ namespace Kerobot.Services.GuildState
|
|||
bool success = await LoadGuildConfiguration(arg.Id);
|
||||
if (!success)
|
||||
{
|
||||
await Kerobot.GuildLogAsync(arg.Id, GuildLogSource,
|
||||
await BotClient.GuildLogAsync(arg.Id, GuildLogSource,
|
||||
"Configuration was not reloaded due to the previously stated error(s).");
|
||||
}
|
||||
else
|
||||
{
|
||||
await Kerobot.InstanceLogAsync(false, GuildLogSource,
|
||||
await BotClient.InstanceLogAsync(false, GuildLogSource,
|
||||
$"Configuration successfully refreshed for guild ID {arg.Id}.");
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ namespace Kerobot.Services.GuildState
|
|||
}
|
||||
catch (Exception ex) when (ex is JsonReaderException || ex is InvalidCastException)
|
||||
{
|
||||
await Kerobot.GuildLogAsync(guildId, GuildLogSource,
|
||||
await BotClient.GuildLogAsync(guildId, GuildLogSource,
|
||||
$"A problem exists within the guild configuration: {ex.Message}");
|
||||
|
||||
// Don't update currently loaded state.
|
||||
|
@ -143,7 +143,7 @@ namespace Kerobot.Services.GuildState
|
|||
|
||||
// Create guild state objects for all existing modules
|
||||
var newStates = new Dictionary<Type, StateInfo>();
|
||||
foreach (var mod in Kerobot.Modules)
|
||||
foreach (var mod in BotClient.Modules)
|
||||
{
|
||||
var t = mod.GetType();
|
||||
var tn = t.Name;
|
||||
|
@ -158,9 +158,9 @@ namespace Kerobot.Services.GuildState
|
|||
{
|
||||
Log("Unhandled exception while initializing guild state for module:\n" +
|
||||
$"Module: {tn} | " +
|
||||
$"Guild: {guildId} ({Kerobot.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" +
|
||||
$"Guild: {guildId} ({BotClient.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" +
|
||||
$"```\n{ex.ToString()}\n```", true).Wait();
|
||||
Kerobot.GuildLogAsync(guildId, GuildLogSource,
|
||||
BotClient.GuildLogAsync(guildId, GuildLogSource,
|
||||
"An internal error occurred when attempting to load new configuration. " +
|
||||
"The bot owner has been notified.").Wait();
|
||||
return false;
|
||||
|
@ -168,7 +168,7 @@ namespace Kerobot.Services.GuildState
|
|||
}
|
||||
catch (ModuleLoadException ex)
|
||||
{
|
||||
await Kerobot.GuildLogAsync(guildId, GuildLogSource,
|
||||
await BotClient.GuildLogAsync(guildId, GuildLogSource,
|
||||
$"{tn} has encountered an issue with its configuration: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ namespace Kerobot.Services.GuildState
|
|||
/// </summary>
|
||||
private async Task CreateDatabaseTablesAsync()
|
||||
{
|
||||
using (var db = await Kerobot.GetOpenNpgsqlConnectionAsync())
|
||||
using (var db = await BotClient.GetOpenNpgsqlConnectionAsync())
|
||||
{
|
||||
using (var c = db.CreateCommand())
|
||||
{
|
||||
|
@ -241,7 +241,7 @@ namespace Kerobot.Services.GuildState
|
|||
/// </summary>
|
||||
private string GetDefaultConfiguration()
|
||||
{
|
||||
const string ResourceName = "Kerobot.DefaultGuildConfig.json";
|
||||
const string ResourceName = $"{nameof(RegexBot)}.DefaultGuildConfig.json";
|
||||
|
||||
var a = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
using (var s = a.GetManifestResourceStream(ResourceName))
|
|
@ -1,10 +1,10 @@
|
|||
using Kerobot.Common;
|
||||
using Kerobot.Services.GuildState;
|
||||
using RegexBot.Common;
|
||||
using RegexBot.Services.GuildState;
|
||||
using System;
|
||||
|
||||
namespace Kerobot
|
||||
namespace RegexBot
|
||||
{
|
||||
partial class Kerobot
|
||||
partial class RegexbotClient
|
||||
{
|
||||
private GuildStateService _svcGuildState;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace Kerobot.Services.GuildState
|
||||
namespace RegexBot.Services.GuildState
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a guild state object and other useful metadata in regards to it.
|
|
@ -1,6 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kerobot.Services
|
||||
namespace RegexBot.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for Kerobot services.
|
||||
|
@ -11,11 +11,11 @@ namespace Kerobot.Services
|
|||
/// </remarks>
|
||||
internal abstract class Service
|
||||
{
|
||||
public Kerobot Kerobot { get; }
|
||||
public RegexbotClient BotClient { get; }
|
||||
|
||||
public string Name => this.GetType().Name;
|
||||
|
||||
public Service(Kerobot kb) => Kerobot = kb;
|
||||
public Service(RegexbotClient bot) => BotClient = bot;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a log message.
|
||||
|
@ -23,6 +23,6 @@ namespace Kerobot.Services
|
|||
/// <param name="message">Logging message contents.</param>
|
||||
/// <param name="report">Determines if the log message should be sent to a reporting channel.</param>
|
||||
/// <returns></returns>
|
||||
protected Task Log(string message, bool report = false) => Kerobot.InstanceLogAsync(report, Name, message);
|
||||
protected Task Log(string message, bool report = false) => BotClient.InstanceLogAsync(report, Name, message);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue