diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..653c3205 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# IDE0003: Remove qualification +dotnet_diagnostic.IDE0003.severity = silent diff --git a/MikuSharp.sln b/MikuSharp.sln index 6f07b478..7a4bdb9f 100644 --- a/MikuSharp.sln +++ b/MikuSharp.sln @@ -7,6 +7,27 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MikuSharp", "MikuSharp\Miku EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NicoNicoNii", "NicoNicoNii\NicoNicoNii\NicoNicoNii.csproj", "{38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4CB3E9BD-AA4F-4000-9595-03AC74654E5A}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Lavalink", "..\DisCatSharp\DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp", "DisCatSharp", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp", "..\DisCatSharp\DisCatSharp\DisCatSharp.csproj", "{5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.ApplicationCommands", "..\DisCatSharp\DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{32B1A815-0A97-D776-A8DF-974D2113DD5F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Common", "..\DisCatSharp\DisCatSharp.Common\DisCatSharp.Common.csproj", "{C58808B1-4112-1650-4864-88B8A5BCE1E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.CommandsNext", "..\DisCatSharp\DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{9894841A-DFE9-21E9-8FE9-E3486D6E28BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Interactivity", "..\DisCatSharp\DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Experimental", "..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj", "{71DC621E-D332-F94C-3F34-65A75DE657D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,10 +42,47 @@ Global {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Release|Any CPU.Build.0 = Release|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Release|Any CPU.Build.0 = Release|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Release|Any CPU.Build.0 = Release|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Release|Any CPU.Build.0 = Release|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Release|Any CPU.Build.0 = Release|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Release|Any CPU.Build.0 = Release|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.Build.0 = Release|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {32B1A815-0A97-D776-A8DF-974D2113DD5F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {C58808B1-4112-1650-4864-88B8A5BCE1E9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {71DC621E-D332-F94C-3F34-65A75DE657D3} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C76757C4-CC79-4C18-83BD-40F37AC98C1A} EndGlobalSection diff --git a/MikuSharp/.editorconfig b/MikuSharp/.editorconfig new file mode 100644 index 00000000..a80a55bf --- /dev/null +++ b/MikuSharp/.editorconfig @@ -0,0 +1,3957 @@ +root = true +#### Core EditorConfig Options #### + +[*] + +charset = utf-8 +trim_trailing_whitespace = true + +indent_size = 4 +indent_style = tab +tab_width = 4 + +end_of_line = lf +insert_final_newline = true + +[*.yml] +intend_size = 2 +indent_style = space +insert_final_newline = false + +[*.sln] +indent_style = tab +tab_width = 4 + +[*.cs] +dotnet_code_quality_unused_parameters = non_public +dotnet_remove_unnecessary_suppression_exclusions = none +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_constructors = false:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning +csharp_style_expression_bodied_methods = when_on_single_line:warning +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_prefer_not_pattern = true:warning +csharp_style_prefer_pattern_matching = false +csharp_style_prefer_switch_expression = true +csharp_style_conditional_delegate_call = false +csharp_prefer_static_local_function = true:warning +csharp_prefer_braces = false:suggestion +csharp_prefer_simple_using_statement = true +csharp_prefer_simple_default_expression = true:warning +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = false:warning +csharp_style_unused_value_assignment_preference = discard_variable:silent +csharp_style_unused_value_expression_statement_preference = discard_variable +csharp_new_line_before_members_in_anonymous_types = true +csharp_indent_block_contents = true +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_preserve_single_line_statements = true +dotnet_naming_rule.interface_should_be_begins_with_i.severity = error +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.private_static_readonly_fields_convention.severity = error +dotnet_naming_rule.private_static_readonly_fields_convention.symbols = private_static_fields_readonly +dotnet_naming_rule.private_static_readonly_fields_convention.style = private_static_camel_case +dotnet_naming_rule.private_static_fields_convention.severity = error +dotnet_naming_rule.private_static_fields_convention.symbols = private_static_field_props +dotnet_naming_rule.private_static_fields_convention.style = private_static_camel_case +dotnet_naming_rule.readonly.severity = error +dotnet_naming_rule.readonly.symbols = readonly +dotnet_naming_rule.readonly.style = underscore_prefixed_camel_case +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.severity = error +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.symbols = private_fields +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.style = underscore_prefixed_camel_case +dotnet_naming_rule.types_should_be_pascal_case.severity = error +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.const_fields_should_be_all_upper.severity = error +dotnet_naming_rule.const_fields_should_be_all_upper.symbols = const_fields +dotnet_naming_rule.const_fields_should_be_all_upper.style = constant_style +dotnet_naming_rule.constants_should_be_upper_case.severity = error +dotnet_naming_rule.constants_should_be_upper_case.symbols = constants +dotnet_naming_rule.constants_should_be_upper_case.style = constant_style +dotnet_naming_rule.private_props_not_allowed.severity = error +dotnet_naming_rule.private_props_not_allowed.symbols = private_prop +dotnet_naming_rule.private_props_not_allowed.style = constant_style +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.private_static_fields_readonly.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_readonly.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_readonly.required_modifiers = static, readonly +dotnet_naming_symbols.private_static_field_props.applicable_kinds = field, property +dotnet_naming_symbols.private_static_field_props.applicable_accessibilities = private +dotnet_naming_symbols.private_static_field_props.required_modifiers = static +dotnet_naming_symbols.private_prop.applicable_kinds = property +dotnet_naming_symbols.private_prop.applicable_accessibilities = private +dotnet_naming_symbols.private_prop.required_modifiers = +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const +dotnet_naming_symbols.const_fields.applicable_kinds = field, property +dotnet_naming_symbols.const_fields.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.const_fields.required_modifiers = const +dotnet_naming_symbols.readonly.applicable_kinds = field, property +dotnet_naming_symbols.readonly.applicable_accessibilities = private +dotnet_naming_symbols.readonly.required_modifiers = readonly +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, private_protected +dotnet_naming_symbols.private_fields.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_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 +dotnet_naming_style.underscore_prefixed_camel_case.required_prefix = _ +dotnet_naming_style.underscore_prefixed_camel_case.required_suffix = +dotnet_naming_style.underscore_prefixed_camel_case.word_separator = +dotnet_naming_style.underscore_prefixed_camel_case.capitalization = camel_case +dotnet_naming_style.private_static_camel_case.required_prefix = s_ +dotnet_naming_style.private_static_camel_case.capitalization = camel_case +dotnet_naming_style.constant_style.required_prefix = +dotnet_naming_style.constant_style.required_suffix = +dotnet_naming_style.constant_style.word_separator = _ +dotnet_naming_style.constant_style.capitalization = all_upper +csharp_indent_braces = false +csharp_indent_switch_labels = true +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion +csharp_preserve_single_line_blocks = true +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_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 +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:warning +csharp_using_directive_placement = outside_namespace:error +dotnet_diagnostic.ide0161.severity = warning +dotnet_diagnostic.bc40000.severity = warning +dotnet_diagnostic.bc400005.severity = warning +dotnet_diagnostic.bc40008.severity = warning +dotnet_diagnostic.bc40056.severity = warning +dotnet_diagnostic.bc42016.severity = warning +dotnet_diagnostic.bc42024.severity = warning +dotnet_diagnostic.bc42025.severity = warning +dotnet_diagnostic.bc42104.severity = warning +dotnet_diagnostic.bc42105.severity = warning +dotnet_diagnostic.bc42106.severity = warning +dotnet_diagnostic.bc42107.severity = warning +dotnet_diagnostic.bc42304.severity = warning +dotnet_diagnostic.bc42309.severity = warning +dotnet_diagnostic.bc42322.severity = warning +dotnet_diagnostic.bc42349.severity = warning +dotnet_diagnostic.bc42353.severity = warning +dotnet_diagnostic.bc42354.severity = warning +dotnet_diagnostic.bc42355.severity = warning +dotnet_diagnostic.bc42356.severity = warning +dotnet_diagnostic.bc42358.severity = warning +dotnet_diagnostic.bc42504.severity = warning +dotnet_diagnostic.bc42505.severity = warning +dotnet_diagnostic.ca2252.severity = error +dotnet_diagnostic.cs0067.severity = warning +dotnet_diagnostic.cs0078.severity = warning +dotnet_diagnostic.cs0108.severity = warning +dotnet_diagnostic.cs0109.severity = warning +dotnet_diagnostic.cs0114.severity = warning +dotnet_diagnostic.cs0162.severity = warning +dotnet_diagnostic.cs0164.severity = warning +dotnet_diagnostic.cs0168.severity = warning +dotnet_diagnostic.cs0169.severity = warning +dotnet_diagnostic.cs0183.severity = warning +dotnet_diagnostic.cs0184.severity = warning +dotnet_diagnostic.cs0197.severity = warning +dotnet_diagnostic.cs0219.severity = warning +dotnet_diagnostic.cs0252.severity = warning +dotnet_diagnostic.cs0253.severity = warning +dotnet_diagnostic.cs0282.severity = warning +dotnet_diagnostic.cs0414.severity = warning +dotnet_diagnostic.cs0420.severity = warning +dotnet_diagnostic.cs0458.severity = warning +dotnet_diagnostic.cs0464.severity = warning +dotnet_diagnostic.cs0465.severity = warning +dotnet_diagnostic.cs0469.severity = warning +dotnet_diagnostic.cs0472.severity = warning +dotnet_diagnostic.cs0612.severity = warning +dotnet_diagnostic.cs0618.severity = warning +dotnet_diagnostic.cs0628.severity = warning +dotnet_diagnostic.cs0642.severity = warning +dotnet_diagnostic.cs0649.severity = warning +dotnet_diagnostic.cs0652.severity = warning +dotnet_diagnostic.cs0657.severity = warning +dotnet_diagnostic.cs0658.severity = warning +dotnet_diagnostic.cs0659.severity = warning +dotnet_diagnostic.cs0660.severity = warning +dotnet_diagnostic.cs0661.severity = warning +dotnet_diagnostic.cs0665.severity = warning +dotnet_diagnostic.cs0672.severity = warning +dotnet_diagnostic.cs0675.severity = warning +dotnet_diagnostic.cs0693.severity = warning +dotnet_diagnostic.cs1030.severity = warning +dotnet_diagnostic.cs1058.severity = warning +dotnet_diagnostic.cs1066.severity = warning +dotnet_diagnostic.cs1522.severity = warning +dotnet_diagnostic.cs1570.severity = warning +dotnet_diagnostic.cs1571.severity = warning +dotnet_diagnostic.cs1572.severity = warning +dotnet_diagnostic.cs1573.severity = warning +dotnet_diagnostic.cs1574.severity = warning +dotnet_diagnostic.cs1580.severity = warning +dotnet_diagnostic.cs1581.severity = warning +dotnet_diagnostic.cs1584.severity = warning +dotnet_diagnostic.cs1587.severity = warning +dotnet_diagnostic.cs1589.severity = warning +dotnet_diagnostic.cs1590.severity = warning +dotnet_diagnostic.cs1591.severity = warning +dotnet_diagnostic.cs1592.severity = warning +dotnet_diagnostic.cs1710.severity = warning +dotnet_diagnostic.cs1711.severity = warning +dotnet_diagnostic.cs1712.severity = warning +dotnet_diagnostic.cs1717.severity = warning +dotnet_diagnostic.cs1723.severity = warning +dotnet_diagnostic.cs1911.severity = warning +dotnet_diagnostic.cs1957.severity = warning +dotnet_diagnostic.cs1981.severity = warning +dotnet_diagnostic.cs1998.severity = warning +dotnet_diagnostic.cs4014.severity = warning +dotnet_diagnostic.cs7022.severity = warning +dotnet_diagnostic.cs7023.severity = warning +dotnet_diagnostic.cs7095.severity = warning +dotnet_diagnostic.cs8073.severity = warning +dotnet_diagnostic.cs8094.severity = warning +dotnet_diagnostic.cs8123.severity = warning +dotnet_diagnostic.cs8321.severity = warning +dotnet_diagnostic.cs8383.severity = warning +dotnet_diagnostic.cs8424.severity = warning +dotnet_diagnostic.cs8425.severity = warning +dotnet_diagnostic.cs8500.severity = warning +dotnet_diagnostic.cs8509.severity = warning +dotnet_diagnostic.cs8519.severity = warning +dotnet_diagnostic.cs8520.severity = warning +dotnet_diagnostic.cs8524.severity = warning +dotnet_diagnostic.cs8597.severity = warning +dotnet_diagnostic.cs8600.severity = warning +dotnet_diagnostic.cs8601.severity = warning +dotnet_diagnostic.cs8602.severity = warning +dotnet_diagnostic.cs8603.severity = warning +dotnet_diagnostic.cs8604.severity = warning +dotnet_diagnostic.cs8605.severity = warning +dotnet_diagnostic.cs8607.severity = warning +dotnet_diagnostic.cs8608.severity = warning +dotnet_diagnostic.cs8609.severity = warning +dotnet_diagnostic.cs8610.severity = warning +dotnet_diagnostic.cs8611.severity = warning +dotnet_diagnostic.cs8612.severity = warning +dotnet_diagnostic.cs8613.severity = warning +dotnet_diagnostic.cs8614.severity = warning +dotnet_diagnostic.cs8615.severity = warning +dotnet_diagnostic.cs8616.severity = warning +dotnet_diagnostic.cs8617.severity = warning +dotnet_diagnostic.cs8618.severity = warning +dotnet_diagnostic.cs8619.severity = warning +dotnet_diagnostic.cs8620.severity = warning +dotnet_diagnostic.cs8621.severity = warning +dotnet_diagnostic.cs8622.severity = warning +dotnet_diagnostic.cs8624.severity = warning +dotnet_diagnostic.cs8625.severity = warning +dotnet_diagnostic.cs8629.severity = warning +dotnet_diagnostic.cs8631.severity = warning +dotnet_diagnostic.cs8632.severity = warning +dotnet_diagnostic.cs8633.severity = warning +dotnet_diagnostic.cs8634.severity = warning +dotnet_diagnostic.cs8643.severity = warning +dotnet_diagnostic.cs8644.severity = warning +dotnet_diagnostic.cs8645.severity = warning +dotnet_diagnostic.cs8655.severity = warning +dotnet_diagnostic.cs8656.severity = warning +dotnet_diagnostic.cs8667.severity = warning +dotnet_diagnostic.cs8669.severity = warning +dotnet_diagnostic.cs8670.severity = warning +dotnet_diagnostic.cs8714.severity = warning +dotnet_diagnostic.cs8762.severity = warning +dotnet_diagnostic.cs8763.severity = warning +dotnet_diagnostic.cs8764.severity = warning +dotnet_diagnostic.cs8765.severity = warning +dotnet_diagnostic.cs8766.severity = warning +dotnet_diagnostic.cs8767.severity = warning +dotnet_diagnostic.cs8768.severity = warning +dotnet_diagnostic.cs8769.severity = warning +dotnet_diagnostic.cs8770.severity = warning +dotnet_diagnostic.cs8774.severity = warning +dotnet_diagnostic.cs8775.severity = warning +dotnet_diagnostic.cs8776.severity = warning +dotnet_diagnostic.cs8777.severity = warning +dotnet_diagnostic.cs8794.severity = warning +dotnet_diagnostic.cs8819.severity = warning +dotnet_diagnostic.cs8824.severity = warning +dotnet_diagnostic.cs8825.severity = warning +dotnet_diagnostic.cs8846.severity = warning +dotnet_diagnostic.cs8847.severity = warning +dotnet_diagnostic.cs8851.severity = warning +dotnet_diagnostic.cs8860.severity = warning +dotnet_diagnostic.cs8892.severity = warning +dotnet_diagnostic.cs8907.severity = warning +dotnet_diagnostic.cs8947.severity = warning +dotnet_diagnostic.cs8960.severity = warning +dotnet_diagnostic.cs8961.severity = warning +dotnet_diagnostic.cs8962.severity = warning +dotnet_diagnostic.cs8963.severity = warning +dotnet_diagnostic.cs8965.severity = warning +dotnet_diagnostic.cs8966.severity = warning +dotnet_diagnostic.cs8971.severity = warning +dotnet_diagnostic.cs8974.severity = warning +dotnet_diagnostic.cs8981.severity = warning +dotnet_diagnostic.cs9042.severity = warning +dotnet_diagnostic.cs9073.severity = warning +dotnet_diagnostic.cs9074.severity = warning +dotnet_diagnostic.cs9080.severity = warning +dotnet_diagnostic.cs9081.severity = warning +dotnet_diagnostic.cs9082.severity = warning +dotnet_diagnostic.cs9083.severity = warning +dotnet_diagnostic.cs9084.severity = warning +dotnet_diagnostic.cs9085.severity = warning +dotnet_diagnostic.cs9086.severity = warning +dotnet_diagnostic.cs9087.severity = warning +dotnet_diagnostic.cs9088.severity = warning +dotnet_diagnostic.cs9089.severity = warning +dotnet_diagnostic.cs9090.severity = warning +dotnet_diagnostic.cs9091.severity = warning +dotnet_diagnostic.cs9092.severity = warning +dotnet_diagnostic.cs9093.severity = warning +dotnet_diagnostic.cs9094.severity = warning +dotnet_diagnostic.cs9095.severity = warning +dotnet_diagnostic.cs9097.severity = warning +dotnet_diagnostic.cs9099.severity = warning +dotnet_diagnostic.cs9100.severity = warning +dotnet_diagnostic.cs9113.severity = warning +dotnet_diagnostic.wme006.severity = warning +dotnet_naming_rule.constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.constants_rule.severity = warning +dotnet_naming_rule.constants_rule.style = upper_camel_case_style +dotnet_naming_rule.constants_rule.symbols = constants_symbols +dotnet_naming_rule.event_rule.import_to_resharper = as_predefined +dotnet_naming_rule.event_rule.severity = warning +dotnet_naming_rule.event_rule.style = upper_camel_case_style +dotnet_naming_rule.event_rule.symbols = event_symbols +dotnet_naming_rule.interfaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.interfaces_rule.severity = warning +dotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style +dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols +dotnet_naming_rule.locals_rule.import_to_resharper = as_predefined +dotnet_naming_rule.locals_rule.severity = warning +dotnet_naming_rule.locals_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.locals_rule.symbols = locals_symbols +dotnet_naming_rule.local_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_constants_rule.severity = warning +dotnet_naming_rule.local_constants_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols +dotnet_naming_rule.local_functions_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_functions_rule.severity = warning +dotnet_naming_rule.local_functions_rule.style = upper_camel_case_style +dotnet_naming_rule.local_functions_rule.symbols = local_functions_symbols +dotnet_naming_rule.method_rule.import_to_resharper = as_predefined +dotnet_naming_rule.method_rule.severity = warning +dotnet_naming_rule.method_rule.style = upper_camel_case_style +dotnet_naming_rule.method_rule.symbols = method_symbols +dotnet_naming_rule.parameters_rule.import_to_resharper = as_predefined +dotnet_naming_rule.parameters_rule.severity = warning +dotnet_naming_rule.parameters_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.parameters_rule.symbols = parameters_symbols +dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_props_not_allowed_rule.import_to_resharper = True +dotnet_naming_rule.private_props_not_allowed_rule.resharper_description = private_props_not_allowed +dotnet_naming_rule.private_props_not_allowed_rule.resharper_guid = 5463b36e-97e1-4c30-9cce-30a590ec863e +dotnet_naming_rule.private_props_not_allowed_rule.resharper_style = s_ + aaBb_AaBb, AA_BB +dotnet_naming_rule.private_props_not_allowed_rule.severity = warning +dotnet_naming_rule.private_props_not_allowed_rule.style = s_lower_camel_case_underscore_tolerant_style +dotnet_naming_rule.private_props_not_allowed_rule.symbols = private_props_not_allowed_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.property_rule.import_to_resharper = as_predefined +dotnet_naming_rule.property_rule.severity = warning +dotnet_naming_rule.property_rule.style = upper_camel_case_style +dotnet_naming_rule.property_rule.symbols = property_symbols +dotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.public_fields_rule.severity = warning +dotnet_naming_rule.public_fields_rule.style = upper_camel_case_style +dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols +dotnet_naming_rule.static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.static_readonly_rule.severity = warning +dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols +dotnet_naming_rule.types_and_namespaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.types_and_namespaces_rule.severity = warning +dotnet_naming_rule.types_and_namespaces_rule.style = upper_camel_case_style +dotnet_naming_rule.types_and_namespaces_rule.symbols = types_and_namespaces_symbols +dotnet_naming_rule.type_parameters_rule.import_to_resharper = as_predefined +dotnet_naming_rule.type_parameters_rule.severity = warning +dotnet_naming_rule.type_parameters_rule.style = t_upper_camel_case_style +dotnet_naming_rule.type_parameters_rule.symbols = type_parameters_symbols +dotnet_naming_style.i_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.i_upper_camel_case_style.required_prefix = I +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.lower_camel_case_style.required_prefix = _ +dotnet_naming_style.lower_camel_case_style_1.capitalization = camel_case +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.capitalization = camel_case +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.required_prefix = s_ +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.word_separator = _ +dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.t_upper_camel_case_style.required_prefix = T +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.constants_symbols.applicable_kinds = field +dotnet_naming_symbols.constants_symbols.required_modifiers = const +dotnet_naming_symbols.event_symbols.applicable_accessibilities = * +dotnet_naming_symbols.event_symbols.applicable_kinds = event +dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface +dotnet_naming_symbols.locals_symbols.applicable_accessibilities = * +dotnet_naming_symbols.locals_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.required_modifiers = const +dotnet_naming_symbols.local_functions_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_functions_symbols.applicable_kinds = local_function +dotnet_naming_symbols.method_symbols.applicable_accessibilities = * +dotnet_naming_symbols.method_symbols.applicable_kinds = method +dotnet_naming_symbols.parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.parameters_symbols.applicable_kinds = parameter +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_props_not_allowed_symbols.applicable_accessibilities = local,private +dotnet_naming_symbols.private_props_not_allowed_symbols.applicable_kinds = property +dotnet_naming_symbols.private_props_not_allowed_symbols.resharper_applicable_kinds = property +dotnet_naming_symbols.private_props_not_allowed_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.property_symbols.applicable_accessibilities = * +dotnet_naming_symbols.property_symbols.applicable_kinds = property +dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate +dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true +dotnet_style_readonly_field = true:warning +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:warning +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning +dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_null_propagation = true:warning +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:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_return = true:warning +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true +file_header_template = + +# ReSharper properties +resharper_accessor_owner_body = expression_body +resharper_alignment_tab_fill_style = use_spaces +resharper_align_linq_query = true +resharper_align_multiline_argument = false +resharper_align_multiline_array_and_object_initializer = false +resharper_align_multiline_array_initializer = false +resharper_align_multiline_binary_patterns = false +resharper_align_multiline_comments = true +resharper_align_multiline_ctor_init = true +resharper_align_multiline_expression = true +resharper_align_multiline_expression_braces = false +resharper_align_multiline_extends_list = false +resharper_align_multiline_implements_list = true +resharper_align_multiline_list_pattern = false +resharper_align_multiline_property_pattern = false +resharper_align_multiline_statement_conditions = false +resharper_align_multiline_switch_expression = false +resharper_align_multiline_type_argument = false +resharper_align_multiline_type_parameter = false +resharper_align_multline_type_parameter_constrains = false +resharper_align_multline_type_parameter_list = false +resharper_align_tuple_components = false +resharper_align_union_type_usage = true +resharper_allow_alias = true +resharper_allow_comment_after_lbrace = false +resharper_allow_far_alignment = false +resharper_always_use_end_of_line_brace_style = false +resharper_apply_auto_detected_rules = true +resharper_apply_on_completion = true +resharper_arguments_anonymous_function = positional +resharper_arguments_literal = positional +resharper_arguments_named = positional +resharper_arguments_other = positional +resharper_arguments_skip_single = false +resharper_arguments_string_literal = positional +resharper_attribute_style = do_not_touch +resharper_autodetect_indent_settings = true +resharper_blank_lines_after_access_specifier = 0 +resharper_blank_lines_after_block_statements = 1 +resharper_blank_lines_after_case = 0 +resharper_blank_lines_after_control_transfer_statements = 0 +resharper_blank_lines_after_file_scoped_namespace_directive = 1 +resharper_blank_lines_after_imports = 1 +resharper_blank_lines_after_multiline_statements = 0 +resharper_blank_lines_after_options = 1 +resharper_blank_lines_after_start_comment = 1 +resharper_blank_lines_after_using_list = 1 +resharper_blank_lines_around_accessor = 0 +resharper_blank_lines_around_auto_property = 1 +resharper_blank_lines_around_block_case_section = 0 +resharper_blank_lines_around_class_definition = 1 +resharper_blank_lines_around_field = 1 +resharper_blank_lines_around_function_declaration = 0 +resharper_blank_lines_around_function_definition = 1 +resharper_blank_lines_around_global_attribute = 1 +resharper_blank_lines_around_invocable = 1 +resharper_blank_lines_around_local_method = 1 +resharper_blank_lines_around_multiline_case_section = 0 +resharper_blank_lines_around_namespace = 1 +resharper_blank_lines_around_other_declaration = 0 +resharper_blank_lines_around_property = 1 +resharper_blank_lines_around_razor_functions = 1 +resharper_blank_lines_around_razor_helpers = 1 +resharper_blank_lines_around_razor_sections = 1 +resharper_blank_lines_around_region = 1 +resharper_blank_lines_around_single_line_accessor = 0 +resharper_blank_lines_around_single_line_auto_property = 0 +resharper_blank_lines_around_single_line_field = 0 +resharper_blank_lines_around_single_line_function_definition = 0 +resharper_blank_lines_around_single_line_invocable = 0 +resharper_blank_lines_around_single_line_local_method = 1 +resharper_blank_lines_around_single_line_property = 0 +resharper_blank_lines_around_single_line_type = 1 +resharper_blank_lines_around_type = 1 +resharper_blank_lines_before_access_specifier = 1 +resharper_blank_lines_before_block_statements = 0 +resharper_blank_lines_before_case = 0 +resharper_blank_lines_before_control_transfer_statements = 0 +resharper_blank_lines_before_multiline_statements = 0 +resharper_blank_lines_before_single_line_comment = 0 +resharper_blank_lines_inside_namespace = 0 +resharper_blank_lines_inside_region = 1 +resharper_blank_lines_inside_type = 0 +resharper_blank_line_after_pi = true +resharper_braces_for_dowhile = required +resharper_braces_for_fixed = required +resharper_braces_for_for = not_required +resharper_braces_for_foreach = not_required +resharper_braces_for_ifelse = not_required +resharper_braces_for_lock = required +resharper_braces_for_using = required +resharper_braces_for_while = not_required +resharper_braces_redundant = true +resharper_break_template_declaration = line_break +resharper_builtin_type_apply_to_native_integer = false +resharper_can_use_global_alias = false +resharper_configure_await_analysis_mode = disabled +resharper_constructor_or_destructor_body = block_body +resharper_continuous_indent_multiplier = 1 +resharper_continuous_line_indent = single +resharper_cpp_align_first_arg_by_paren = false +resharper_cpp_align_multiline_binary_expressions_chain = false +resharper_cpp_align_multiline_calls_chain = true +resharper_cpp_align_multiline_for_stmt = true +resharper_cpp_align_multiline_parameter = true +resharper_cpp_align_multiple_declaration = true +resharper_cpp_align_ternary = align_not_nested +resharper_cpp_anonymous_method_declaration_braces = next_line +resharper_cpp_case_block_braces = next_line_shifted_2 +resharper_cpp_empty_block_style = multiline +resharper_cpp_indent_switch_labels = false +resharper_cpp_invocable_declaration_braces = next_line +resharper_cpp_keep_blank_lines_in_declarations = 2 +resharper_cpp_max_line_length = 120 +resharper_cpp_new_line_before_catch = true +resharper_cpp_new_line_before_else = true +resharper_cpp_new_line_before_while = true +resharper_cpp_other_braces = next_line +resharper_cpp_space_after_unary_operator = false +resharper_cpp_space_around_binary_operator = true +resharper_cpp_type_declaration_braces = next_line +resharper_cpp_wrap_arguments_style = wrap_if_long +resharper_cpp_wrap_lines = true +resharper_cpp_wrap_parameters_style = wrap_if_long +resharper_csharp_align_first_arg_by_paren = false +resharper_csharp_align_multiline_binary_expressions_chain = true +resharper_csharp_align_multiline_calls_chain = false +resharper_csharp_align_multiline_for_stmt = false +resharper_csharp_align_multiline_parameter = false +resharper_csharp_align_multiple_declaration = false +resharper_csharp_empty_block_style = together +resharper_csharp_keep_blank_lines_in_declarations = 1 +resharper_csharp_max_line_length = 1000 +resharper_csharp_naming_rule.enum_member = AaBb +resharper_csharp_naming_rule.method_property_event = AaBb +resharper_csharp_naming_rule.other = AaBb +resharper_csharp_new_line_before_while = true +resharper_csharp_prefer_qualified_reference = false +resharper_csharp_space_after_unary_operator = false +resharper_csharp_stick_comment = false +resharper_csharp_wrap_arguments_style = wrap_if_long +resharper_csharp_wrap_chained_binary_expressions = wrap_if_long +resharper_csharp_wrap_lines = false +resharper_csharp_wrap_parameters_style = wrap_if_long +resharper_css_brace_style = end_of_line +resharper_css_keep_blank_lines_between_declarations = 1 +resharper_css_max_line_length = 120 +resharper_css_wrap_lines = true +resharper_cxxcli_property_declaration_braces = next_line +resharper_declarations_style = separate_lines +resharper_default_exception_variable_name = e +resharper_default_value_when_type_evident = default_literal +resharper_default_value_when_type_not_evident = default_literal +resharper_delete_quotes_from_solid_values = false +resharper_disable_blank_line_changes = false +resharper_disable_formatter = false +resharper_disable_indenter = false +resharper_disable_int_align = false +resharper_disable_line_break_changes = false +resharper_disable_line_break_removal = false +resharper_disable_space_changes = false +resharper_disable_space_changes_before_trailing_comment = false +resharper_dont_remove_extra_blank_lines = false +resharper_enable_wrapping = false +resharper_enforce_line_ending_style = true +resharper_event_handler_pattern_long = $object$On$event$ +resharper_event_handler_pattern_short = On$event$ +resharper_export_declaration_braces = next_line +resharper_expression_braces = inside +resharper_expression_pars = inside +resharper_extra_spaces = remove_all +resharper_force_attribute_style = join +resharper_force_chop_compound_do_expression = false +resharper_force_chop_compound_if_expression = false +resharper_force_chop_compound_while_expression = false +resharper_force_control_statements_braces = do_not_change +resharper_force_linebreaks_inside_complex_literals = true +resharper_force_variable_declarations_on_new_line = false +resharper_formatter_off_tag = +resharper_formatter_on_tag = +resharper_formatter_tags_accept_regexp = false +resharper_formatter_tags_enabled = false +resharper_format_leading_spaces_decl = false +resharper_free_block_braces = next_line +resharper_function_declaration_return_type_style = do_not_change +resharper_function_definition_return_type_style = do_not_change +resharper_generator_mode = false +resharper_html_attribute_indent = align_by_first_attribute +resharper_html_linebreak_before_elements = body,div,p,form,h1,h2,h3 +resharper_html_max_blank_lines_between_tags = 2 +resharper_html_max_line_length = 120 +resharper_html_pi_attribute_style = on_single_line +resharper_html_space_before_self_closing = false +resharper_html_wrap_lines = true +resharper_ignore_space_preservation = false +resharper_include_prefix_comment_in_indent = false +resharper_indent_access_specifiers_from_class = false +resharper_indent_aligned_ternary = true +resharper_indent_anonymous_method_block = false +resharper_indent_braces_inside_statement_conditions = true +resharper_indent_case_from_select = true +resharper_indent_child_elements = OneIndent +resharper_indent_class_members_from_access_specifiers = false +resharper_indent_comment = true +resharper_indent_export_declaration_members = true +resharper_indent_inside_namespace = true +resharper_indent_invocation_pars = inside +resharper_indent_left_par_inside_expression = false +resharper_indent_member_initializer_list = true +resharper_indent_method_decl_pars = inside +resharper_indent_nested_fixed_stmt = false +resharper_indent_nested_foreach_stmt = false +resharper_indent_nested_for_stmt = false +resharper_indent_nested_lock_stmt = false +resharper_indent_nested_usings_stmt = false +resharper_indent_nested_while_stmt = false +resharper_indent_pars = inside +resharper_align_first_arg_by_paren = false +resharper_csharp_align_multiline_argument = false +resharper_csharp_align_multiline_expression = false +resharper_csharp_align_multiline_extends_list = false +resharper_csharp_allow_far_alignment = true +resharper_csharp_keep_blank_lines_in_code = 1 +resharper_csharp_wrap_after_declaration_lpar = true +resharper_csharp_wrap_before_declaration_rpar = true +resharper_indent_preprocessor_directives = none +resharper_indent_preprocessor_if = no_indent +resharper_indent_preprocessor_other = no_indent +resharper_indent_preprocessor_region = no_indent +resharper_indent_raw_literal_string = indent +resharper_indent_statement_pars = inside +resharper_indent_text = OneIndent +resharper_indent_typearg_angles = inside +resharper_indent_typeparam_angles = inside +resharper_indent_type_constraints = true +resharper_indent_wrapped_function_names = false +resharper_instance_members_qualify_declared_in = this_class, base_class +resharper_int_align = false +resharper_int_align_bitfield_sizes = false +resharper_int_align_comments = false +resharper_int_align_declaration_names = false +resharper_int_align_enum_initializers = false +resharper_int_align_eq = false +resharper_int_align_fix_in_adjacent = true +resharper_js_align_multiline_parameter = false +resharper_js_align_multiple_declaration = false +resharper_js_align_ternary = none +resharper_js_brace_style = end_of_line +resharper_js_empty_block_style = multiline +resharper_js_indent_switch_labels = false +resharper_js_keep_blank_lines_between_declarations = 2 +resharper_js_max_line_length = 120 +resharper_js_new_line_before_catch = false +resharper_js_new_line_before_else = false +resharper_js_new_line_before_finally = false +resharper_js_new_line_before_while = false +resharper_js_space_around_binary_operator = true +resharper_js_stick_comment = true +resharper_js_wrap_arguments_style = chop_if_long +resharper_js_wrap_chained_binary_expressions = chop_if_long +resharper_js_wrap_lines = true +resharper_js_wrap_parameters_style = chop_if_long +resharper_keep_blank_lines_in_code = 2 +resharper_keep_existing_attribute_arrangement = false +resharper_keep_existing_declaration_block_arrangement = false +resharper_keep_existing_declaration_parens_arrangement = true +resharper_keep_existing_embedded_arrangement = true +resharper_keep_existing_embedded_block_arrangement = false +resharper_keep_existing_enum_arrangement = false +resharper_keep_existing_expr_member_arrangement = true +resharper_keep_existing_invocation_parens_arrangement = true +resharper_keep_existing_list_patterns_arrangement = false +resharper_keep_existing_property_patterns_arrangement = false +resharper_keep_existing_switch_expression_arrangement = true +resharper_keep_nontrivial_alias = true +resharper_keep_user_linebreaks = true +resharper_keep_user_wrapping = true +resharper_linebreaks_around_razor_statements = true +resharper_linebreaks_inside_tags_for_elements_longer_than = 2147483647 +resharper_linebreaks_inside_tags_for_elements_with_child_elements = true +resharper_linebreaks_inside_tags_for_multiline_elements = true +resharper_linebreak_before_all_elements = false +resharper_linebreak_before_multiline_elements = true +resharper_linebreak_before_singleline_elements = false +resharper_line_break_after_colon_in_member_initializer_lists = do_not_change +resharper_line_break_after_comma_in_member_initializer_lists = false +resharper_line_break_after_init_statement = do_not_change +resharper_line_break_before_comma_in_member_initializer_lists = false +resharper_line_break_before_requires_clause = do_not_change +resharper_linkage_specification_braces = end_of_line +resharper_linkage_specification_indentation = none +resharper_local_function_body = expression_body +resharper_macro_block_begin = +resharper_macro_block_end = +resharper_max_array_initializer_elements_on_line = 10000 +resharper_max_attribute_length_for_same_line = 50 +resharper_max_enum_members_on_line = 3 +resharper_max_formal_parameters_on_line = 10000 +resharper_max_initializer_elements_on_line = 5 +resharper_max_invocation_arguments_on_line = 10000 +resharper_media_query_style = same_line +resharper_member_initializer_list_style = do_not_change +resharper_method_or_operator_body = expression_body +resharper_min_blank_lines_after_imports = 0 +resharper_min_blank_lines_around_fields = 0 +resharper_min_blank_lines_around_functions = 1 +resharper_min_blank_lines_around_types = 1 +resharper_min_blank_lines_between_declarations = 1 +resharper_namespace_declaration_braces = next_line +resharper_namespace_indentation = all +resharper_nested_ternary_style = expanded +resharper_new_line_before_enumerators = true +resharper_normalize_tag_names = false +resharper_no_indent_inside_elements = html,body,thead,tbody,tfoot +resharper_no_indent_inside_if_element_longer_than = 200 +resharper_null_checking_pattern_style = not_null_pattern +resharper_object_creation_when_type_evident = target_typed +resharper_object_creation_when_type_not_evident = target_typed +resharper_old_engine = false +resharper_options_braces_pointy = false +resharper_outdent_binary_ops = false +resharper_outdent_binary_pattern_ops = false +resharper_outdent_commas = false +resharper_outdent_dots = false +resharper_outdent_namespace_member = false +resharper_outdent_statement_labels = false +resharper_outdent_ternary_ops = false +resharper_parentheses_non_obvious_operations = none, shift, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise +resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence +resharper_parentheses_same_type_operations = false +resharper_pi_attributes_indent = align_by_first_attribute +resharper_place_accessorholder_attribute_on_same_line = false +resharper_place_accessor_attribute_on_same_line = false +resharper_place_class_decorator_on_the_same_line = false +resharper_place_comments_at_first_column = false +resharper_place_constructor_initializer_on_same_line = false +resharper_place_each_decorator_on_new_line = false +resharper_place_event_attribute_on_same_line = false +resharper_place_expr_accessor_on_single_line = true +resharper_place_expr_method_on_single_line = false +resharper_place_expr_property_on_single_line = false +resharper_place_field_attribute_on_same_line = false +resharper_place_field_decorator_on_the_same_line = false +resharper_place_linq_into_on_new_line = true +resharper_place_method_attribute_on_same_line = false +resharper_place_method_decorator_on_the_same_line = false +resharper_place_namespace_definitions_on_same_line = false +resharper_place_property_attribute_on_same_line = false +resharper_place_property_decorator_on_the_same_line = false +resharper_place_record_field_attribute_on_same_line = true +resharper_place_simple_case_statement_on_same_line = false +resharper_place_simple_embedded_statement_on_same_line = false +resharper_place_simple_initializer_on_single_line = true +resharper_place_simple_list_pattern_on_single_line = true +resharper_place_simple_property_pattern_on_single_line = true +resharper_place_simple_switch_expression_on_single_line = false +resharper_place_template_args_on_new_line = false +resharper_place_type_attribute_on_same_line = false +resharper_place_type_constraints_on_same_line = true +resharper_prefer_explicit_discard_declaration = false +resharper_prefer_separate_deconstructed_variables_declaration = false +resharper_preserve_spaces_inside_tags = pre,textarea +resharper_properties_style = separate_lines_for_nonsingle +resharper_protobuf_brace_style = end_of_line +resharper_protobuf_empty_block_style = together_same_line +resharper_protobuf_max_line_length = 120 +resharper_protobuf_wrap_lines = true +resharper_qualified_using_at_nested_scope = false +resharper_quote_style = doublequoted +resharper_razor_prefer_qualified_reference = true +resharper_remove_blank_lines_near_braces = false +resharper_remove_blank_lines_near_braces_in_code = true +resharper_remove_blank_lines_near_braces_in_declarations = true +resharper_remove_this_qualifier = true +resharper_requires_expression_braces = next_line +resharper_resx_attribute_indent = single_indent +resharper_resx_linebreak_before_elements = +resharper_resx_max_blank_lines_between_tags = 0 +resharper_resx_max_line_length = 2147483647 +resharper_resx_pi_attribute_style = do_not_touch +resharper_resx_space_before_self_closing = false +resharper_resx_wrap_lines = false +resharper_resx_wrap_tags_and_pi = false +resharper_resx_wrap_text = false +resharper_selector_style = same_line +resharper_show_autodetect_configure_formatting_tip = false +resharper_simple_blocks = do_not_change +resharper_simple_block_style = do_not_change +resharper_simple_case_statement_style = do_not_change +resharper_simple_embedded_statement_style = do_not_change +resharper_single_statement_function_style = do_not_change +resharper_sort_attributes = false +resharper_sort_class_selectors = false +resharper_sort_usings = true +resharper_sort_usings_lowercase_first = false +resharper_spaces_around_eq_in_attribute = false +resharper_spaces_around_eq_in_pi_attribute = false +resharper_spaces_inside_tags = false +resharper_space_after_arrow = true +resharper_space_after_attributes = true +resharper_space_after_attribute_target_colon = true +resharper_space_after_cast = false +resharper_space_after_colon = true +resharper_space_after_colon_in_bitfield_declarator = true +resharper_space_after_colon_in_case = true +resharper_space_after_colon_in_inheritance_clause = true +resharper_space_after_colon_in_type_annotation = true +resharper_space_after_comma = true +resharper_space_after_ellipsis_in_parameter_pack = true +resharper_space_after_for_colon = true +resharper_space_after_function_comma = true +resharper_space_after_keywords_in_control_flow_statements = true +resharper_space_after_last_attribute = false +resharper_space_after_last_pi_attribute = false +resharper_space_after_media_colon = true +resharper_space_after_media_comma = true +resharper_space_after_operator_keyword = true +resharper_space_after_operator_not = false +resharper_space_after_property_colon = true +resharper_space_after_property_semicolon = true +resharper_space_after_ptr_in_data_member = true +resharper_space_after_ptr_in_data_members = false +resharper_space_after_ptr_in_method = true +resharper_space_after_ptr_in_nested_declarator = false +resharper_space_after_ref_in_data_member = true +resharper_space_after_ref_in_data_members = false +resharper_space_after_ref_in_method = true +resharper_space_after_selector_comma = true +resharper_space_after_semicolon_in_for_statement = true +resharper_space_after_separator = false +resharper_space_after_ternary_colon = true +resharper_space_after_ternary_quest = true +resharper_space_after_triple_slash = true +resharper_space_after_type_parameter_constraint_colon = true +resharper_space_around_additive_op = true +resharper_space_around_alias_eq = true +resharper_space_around_assignment_op = true +resharper_space_around_assignment_operator = true +resharper_space_around_attribute_match_operator = false +resharper_space_around_deref_in_trailing_return_type = true +resharper_space_around_lambda_arrow = true +resharper_space_around_member_access_operator = false +resharper_space_around_operator = true +resharper_space_around_pipe_or_amper_in_type_usage = true +resharper_space_around_relational_op = true +resharper_space_around_selector_operator = true +resharper_space_around_shift_op = true +resharper_space_around_stmt_colon = true +resharper_space_around_ternary_operator = true +resharper_space_before_array_rank_parentheses = false +resharper_space_before_arrow = true +resharper_space_before_attribute_target_colon = false +resharper_space_before_checked_parentheses = false +resharper_space_before_colon = false +resharper_space_before_colon_in_bitfield_declarator = true +resharper_space_before_colon_in_case = false +resharper_space_before_colon_in_inheritance_clause = true +resharper_space_before_colon_in_type_annotation = false +resharper_space_before_comma = false +resharper_space_before_default_parentheses = false +resharper_space_before_ellipsis_in_parameter_pack = false +resharper_space_before_empty_invocation_parentheses = false +resharper_space_before_empty_method_parentheses = false +resharper_space_before_for_colon = true +resharper_space_before_function_comma = false +resharper_space_before_initializer_braces = false +resharper_space_before_invocation_parentheses = false +resharper_space_before_label_colon = false +resharper_space_before_lambda_parentheses = false +resharper_space_before_media_colon = false +resharper_space_before_media_comma = false +resharper_space_before_method_parentheses = false +resharper_space_before_nameof_parentheses = false +resharper_space_before_new_parentheses = false +resharper_space_before_nullable_mark = false +resharper_space_before_open_square_brackets = false +resharper_space_before_pointer_asterik_declaration = false +resharper_space_before_postfix_operator = false +resharper_space_before_property_colon = false +resharper_space_before_property_semicolon = false +resharper_space_before_ptr_in_abstract_decl = false +resharper_space_before_ptr_in_data_member = false +resharper_space_before_ptr_in_data_members = true +resharper_space_before_ptr_in_method = false +resharper_space_before_ref_in_abstract_decl = false +resharper_space_before_ref_in_data_member = false +resharper_space_before_ref_in_data_members = true +resharper_space_before_ref_in_method = false +resharper_space_before_selector_comma = false +resharper_space_before_semicolon = false +resharper_space_before_semicolon_in_for_statement = false +resharper_space_before_separator = false +resharper_space_before_singleline_accessorholder = true +resharper_space_before_sizeof_parentheses = false +resharper_space_before_template_args = false +resharper_space_before_template_params = true +resharper_space_before_ternary_colon = true +resharper_space_before_ternary_quest = true +resharper_space_before_trailing_comment = true +resharper_space_before_typeof_parentheses = false +resharper_space_before_type_argument_angle = false +resharper_space_before_type_parameters_brackets = false +resharper_space_before_type_parameter_angle = false +resharper_space_before_type_parameter_constraint_colon = true +resharper_space_before_type_parameter_parentheses = true +resharper_space_between_accessors_in_singleline_property = true +resharper_space_between_attribute_sections = true +resharper_space_between_closing_angle_brackets_in_template_args = false +resharper_space_between_empty_square_brackets = false +resharper_space_between_keyword_and_expression = true +resharper_space_between_keyword_and_type = true +resharper_space_between_method_call_empty_parameter_list_parentheses = false +resharper_space_between_method_call_name_and_opening_parenthesis = false +resharper_space_between_method_call_parameter_list_parentheses = false +resharper_space_between_method_declaration_empty_parameter_list_parentheses = false +resharper_space_between_method_declaration_name_and_open_parenthesis = false +resharper_space_between_method_declaration_parameter_list_parentheses = false +resharper_space_between_parentheses_of_control_flow_statements = false +resharper_space_between_square_brackets = false +resharper_space_between_typecast_parentheses = false +resharper_space_colon_after = true +resharper_space_colon_before = false +resharper_space_comma = true +resharper_space_equals = true +resharper_space_inside_braces = true +resharper_space_in_singleline_accessorholder = true +resharper_space_in_singleline_anonymous_method = true +resharper_space_in_singleline_method = true +resharper_space_near_postfix_and_prefix_op = false +resharper_space_within_array_initialization_braces = false +resharper_space_within_array_rank_empty_parentheses = false +resharper_space_within_array_rank_parentheses = false +resharper_space_within_attribute_angles = false +resharper_space_within_attribute_match_brackets = false +resharper_space_within_checked_parentheses = false +resharper_space_within_declaration_parentheses = false +resharper_space_within_default_parentheses = false +resharper_space_within_empty_braces = true +resharper_space_within_empty_initializer_braces = false +resharper_space_within_empty_invocation_parentheses = false +resharper_space_within_empty_method_parentheses = false +resharper_space_within_empty_object_literal_braces = false +resharper_space_within_empty_template_params = false +resharper_space_within_expression_parentheses = false +resharper_space_within_function_parentheses = false +resharper_space_within_import_braces = true +resharper_space_within_initializer_braces = false +resharper_space_within_invocation_parentheses = false +resharper_space_within_media_block = true +resharper_space_within_media_parentheses = false +resharper_space_within_method_parentheses = false +resharper_space_within_nameof_parentheses = false +resharper_space_within_new_parentheses = false +resharper_space_within_object_literal_braces = true +resharper_space_within_parentheses = false +resharper_space_within_property_block = true +resharper_space_within_single_line_array_initializer_braces = true +resharper_space_within_sizeof_parentheses = false +resharper_space_within_slice_pattern = true +resharper_space_within_template_args = false +resharper_space_within_template_argument = false +resharper_space_within_template_params = false +resharper_space_within_tuple_parentheses = false +resharper_space_within_typeof_parentheses = false +resharper_space_within_type_argument_angles = false +resharper_space_within_type_parameters_brackets = false +resharper_space_within_type_parameter_angles = false +resharper_space_within_type_parameter_parentheses = false +resharper_special_else_if_treatment = true +resharper_static_members_qualify_members = none +resharper_static_members_qualify_with = declared_type +resharper_support_vs_event_naming_pattern = true +resharper_termination_style = ensure_semicolon +resharper_toplevel_function_declaration_return_type_style = do_not_change +resharper_toplevel_function_definition_return_type_style = do_not_change +resharper_trailing_comma_in_multiline_lists = false +resharper_trailing_comma_in_singleline_lists = false +resharper_types_braces = end_of_line +resharper_use_continuous_indent_inside_initializer_braces = true +resharper_use_continuous_indent_inside_parens = true +resharper_use_continuous_line_indent_in_expression_braces = false +resharper_use_continuous_line_indent_in_method_pars = false +resharper_use_heuristics_for_body_style = true +resharper_use_indents_from_main_language_in_file = true +resharper_use_indent_from_previous_element = true +resharper_use_indent_from_vs = false +resharper_use_roslyn_logic_for_evident_types = true +resharper_vb_align_multiline_parameter = true +resharper_vb_align_multiple_declaration = true +resharper_vb_keep_blank_lines_in_declarations = 2 +resharper_vb_max_line_length = 120 +resharper_vb_place_field_attribute_on_same_line = true +resharper_vb_place_method_attribute_on_same_line = false +resharper_vb_place_type_attribute_on_same_line = false +resharper_vb_prefer_qualified_reference = false +resharper_vb_space_after_unary_operator = true +resharper_vb_space_around_multiplicative_op = false +resharper_vb_stick_comment = true +resharper_vb_wrap_arguments_style = wrap_if_long +resharper_vb_wrap_lines = true +resharper_vb_wrap_parameters_style = wrap_if_long +resharper_wrap_after_binary_opsign = true +resharper_wrap_after_declaration_lpar = false +resharper_wrap_after_dot = false +resharper_wrap_after_dot_in_method_calls = false +resharper_wrap_after_expression_lbrace = true +resharper_wrap_after_invocation_lpar = false +resharper_wrap_after_property_in_chained_method_calls = false +resharper_wrap_around_elements = true +resharper_wrap_array_initializer_style = chop_if_long +resharper_wrap_array_literals = chop_if_long +resharper_wrap_base_clause_style = wrap_if_long +resharper_wrap_before_arrow_with_expressions = true +resharper_wrap_before_binary_opsign = false +resharper_wrap_before_binary_pattern_op = true +resharper_wrap_before_colon = false +resharper_wrap_before_comma = false +resharper_wrap_before_comma_in_base_clause = false +resharper_wrap_before_declaration_lpar = false +resharper_wrap_before_declaration_rpar = false +resharper_wrap_before_dot = true +resharper_wrap_before_eq = false +resharper_wrap_before_expression_rbrace = true +resharper_wrap_before_extends_colon = false +resharper_wrap_before_first_method_call = false +resharper_wrap_before_first_type_parameter_constraint = false +resharper_wrap_before_invocation_lpar = false +resharper_wrap_before_invocation_rpar = false +resharper_wrap_before_linq_expression = false +resharper_wrap_before_ternary_opsigns = true +resharper_wrap_before_type_parameter_langle = false +resharper_wrap_braced_init_list_style = wrap_if_long +resharper_wrap_chained_binary_patterns = wrap_if_long +resharper_wrap_chained_method_calls = wrap_if_long +resharper_wrap_ctor_initializer_style = wrap_if_long +resharper_wrap_enumeration_style = chop_if_long +resharper_wrap_enum_declaration = chop_always +resharper_wrap_enum_style = do_not_change +resharper_wrap_extends_list_style = wrap_if_long +resharper_wrap_for_stmt_header_style = chop_if_long +resharper_wrap_imports = chop_if_long +resharper_wrap_list_pattern = wrap_if_long +resharper_wrap_multiple_declaration_style = chop_if_long +resharper_wrap_multiple_type_parameter_constraints_style = chop_if_long +resharper_wrap_object_and_collection_initializer_style = chop_always +resharper_wrap_object_literals = chop_if_long +resharper_wrap_property_pattern = chop_if_long +resharper_wrap_switch_expression = chop_always +resharper_wrap_ternary_expr_style = chop_if_long +resharper_wrap_union_type_usage = chop_if_long +resharper_wrap_verbatim_interpolated_strings = no_wrap +resharper_xmldoc_attribute_indent = single_indent +resharper_xmldoc_linebreak_before_elements = summary,remarks,example,returns,param,typeparam,value,para +resharper_xmldoc_max_blank_lines_between_tags = 0 +resharper_xmldoc_max_line_length = 120 +resharper_xmldoc_pi_attribute_style = do_not_touch +resharper_xmldoc_space_before_self_closing = true +resharper_xmldoc_wrap_lines = true +resharper_xmldoc_wrap_tags_and_pi = true +resharper_xmldoc_wrap_text = true +resharper_xml_attribute_indent = align_by_first_attribute +resharper_xml_linebreak_before_elements = +resharper_xml_max_blank_lines_between_tags = 2 +resharper_xml_max_line_length = 120 +resharper_xml_pi_attribute_style = do_not_touch +resharper_xml_space_before_self_closing = true +resharper_xml_wrap_lines = true +resharper_xml_wrap_tags_and_pi = true +resharper_xml_wrap_text = false + +# ReSharper inspection severities +resharper_abstract_class_constructor_can_be_made_protected_highlighting = hint +resharper_access_rights_in_text_highlighting = warning +resharper_access_to_disposed_closure_highlighting = warning +resharper_access_to_for_each_variable_in_closure_highlighting = warning +resharper_access_to_modified_closure_highlighting = warning +resharper_access_to_static_member_via_derived_type_highlighting = warning +resharper_address_of_marshal_by_ref_object_highlighting = warning +resharper_all_underscore_local_parameter_name_highlighting = warning +resharper_amd_dependency_path_problem_highlighting = none +resharper_amd_external_module_highlighting = suggestion +resharper_angular_html_banana_highlighting = warning +resharper_annotate_can_be_null_parameter_highlighting = none +resharper_annotate_can_be_null_type_member_highlighting = none +resharper_annotate_not_null_parameter_highlighting = none +resharper_annotate_not_null_type_member_highlighting = none +resharper_annotation_conflict_in_hierarchy_highlighting = warning +resharper_annotation_redundancy_at_value_type_highlighting = warning +resharper_annotation_redundancy_in_hierarchy_highlighting = warning +resharper_anonymous_object_destructuring_problem_highlighting = warning +resharper_arguments_style_anonymous_function_highlighting = none +resharper_arguments_style_literal_highlighting = none +resharper_arguments_style_named_expression_highlighting = none +resharper_arguments_style_other_highlighting = none +resharper_arguments_style_string_literal_highlighting = none +resharper_arrange_accessor_owner_body_highlighting = suggestion +resharper_arrange_attributes_highlighting = warning +resharper_arrange_constructor_or_destructor_body_highlighting = none +resharper_arrange_default_value_when_type_evident_highlighting = suggestion +resharper_arrange_default_value_when_type_not_evident_highlighting = hint +resharper_arrange_local_function_body_highlighting = none +resharper_arrange_method_or_operator_body_highlighting = none +resharper_arrange_null_checking_pattern_highlighting = hint +resharper_arrange_object_creation_when_type_evident_highlighting = suggestion +resharper_arrange_object_creation_when_type_not_evident_highlighting = hint +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_static_member_qualifier_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_trailing_comma_in_multiline_lists_highlighting = hint +resharper_arrange_trailing_comma_in_singleline_lists_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_arrange_var_keywords_in_deconstructing_declaration_highlighting = suggestion +resharper_asp_content_placeholder_not_resolved_highlighting = error +resharper_asp_custom_page_parser_filter_type_highlighting = warning +resharper_asp_dead_code_highlighting = warning +resharper_asp_entity_highlighting = warning +resharper_asp_image_highlighting = warning +resharper_asp_invalid_control_type_highlighting = error +resharper_asp_not_resolved_highlighting = error +resharper_asp_ods_method_reference_resolve_error_highlighting = error +resharper_asp_resolve_warning_highlighting = warning +resharper_asp_skin_not_resolved_highlighting = error +resharper_asp_tag_attribute_with_optional_value_highlighting = warning +resharper_asp_theme_not_resolved_highlighting = error +resharper_asp_unused_register_directive_highlighting_highlighting = warning +resharper_asp_warning_highlighting = warning +resharper_assigned_value_is_never_used_highlighting = warning +resharper_assigned_value_wont_be_assigned_to_corresponding_field_highlighting = warning +resharper_assignment_instead_of_discard_highlighting = warning +resharper_assignment_in_conditional_expression_highlighting = warning +resharper_assignment_in_condition_expression_highlighting = warning +resharper_assignment_is_fully_discarded_highlighting = warning +resharper_assign_null_to_not_null_attribute_highlighting = warning +resharper_assign_to_constant_highlighting = error +resharper_assign_to_implicit_global_in_function_scope_highlighting = warning +resharper_asxx_path_error_highlighting = warning +resharper_async_converter_async_await_may_be_elided_highlighting_highlighting = none +resharper_async_converter_async_method_naming_highlighting_highlighting = warning +resharper_async_converter_async_wait_highlighting = hint +resharper_async_converter_configure_await_highlighting_highlighting = none +resharper_async_converter_null_return_as_task_highlighting = warning +resharper_async_iterator_invocation_without_await_foreach_highlighting = warning +resharper_async_void_lambda_highlighting = warning +resharper_async_void_method_highlighting = none +resharper_auto_property_can_be_made_get_only_global_highlighting = suggestion +resharper_auto_property_can_be_made_get_only_local_highlighting = suggestion +resharper_bad_attribute_brackets_spaces_highlighting = warning +resharper_bad_braces_spaces_highlighting = warning +resharper_bad_child_statement_indent_highlighting = warning +resharper_bad_colon_spaces_highlighting = warning +resharper_bad_comma_spaces_highlighting = warning +resharper_bad_control_braces_indent_highlighting = warning +resharper_bad_control_braces_line_breaks_highlighting = warning +resharper_bad_declaration_braces_indent_highlighting = warning +resharper_bad_declaration_braces_line_breaks_highlighting = warning +resharper_bad_empty_braces_line_breaks_highlighting = none +resharper_bad_expression_braces_indent_highlighting = warning +resharper_bad_expression_braces_line_breaks_highlighting = warning +resharper_bad_generic_brackets_spaces_highlighting = warning +resharper_bad_indent_highlighting = warning +resharper_bad_linq_line_breaks_highlighting = warning +resharper_bad_list_line_breaks_highlighting = warning +resharper_bad_member_access_spaces_highlighting = warning +resharper_bad_namespace_braces_indent_highlighting = warning +resharper_bad_parens_line_breaks_highlighting = warning +resharper_bad_parens_spaces_highlighting = warning +resharper_bad_preprocessor_indent_highlighting = warning +resharper_bad_semicolon_spaces_highlighting = warning +resharper_bad_spaces_after_keyword_highlighting = warning +resharper_bad_square_brackets_spaces_highlighting = warning +resharper_bad_switch_braces_indent_highlighting = warning +resharper_bad_symbol_spaces_highlighting = warning +resharper_base_member_has_params_highlighting = warning +resharper_base_method_call_with_default_parameter_highlighting = warning +resharper_base_object_equals_is_object_equals_highlighting = warning +resharper_base_object_get_hash_code_call_in_get_hash_code_highlighting = warning +resharper_bitwise_operator_on_enum_without_flags_highlighting = warning +resharper_blazor_editor_required_highlighting = warning +resharper_block_scope_redeclaration_highlighting = error +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_by_ref_argument_is_volatile_field_highlighting = warning +resharper_caller_callee_using_error_highlighting = error +resharper_caller_callee_using_highlighting = warning +resharper_cannot_apply_equality_operator_to_type_highlighting = warning +resharper_can_simplify_dictionary_lookup_with_try_add_highlighting = warning +resharper_can_simplify_dictionary_lookup_with_try_get_value_highlighting = warning +resharper_catch_all_clause_highlighting = none +resharper_center_tag_is_obsolete_highlighting = warning +resharper_check_for_reference_equality_instead_1_highlighting = suggestion +resharper_check_for_reference_equality_instead_2_highlighting = suggestion +resharper_check_for_reference_equality_instead_3_highlighting = suggestion +resharper_check_for_reference_equality_instead_4_highlighting = suggestion +resharper_check_namespace_highlighting = warning +resharper_class_cannot_be_instantiated_highlighting = warning +resharper_class_can_be_sealed_global_highlighting = hint +resharper_class_can_be_sealed_local_highlighting = hint +resharper_class_highlighting = suggestion +resharper_class_never_instantiated_global_highlighting = none +resharper_class_never_instantiated_local_highlighting = suggestion +resharper_class_with_virtual_members_never_inherited_global_highlighting = suggestion +resharper_class_with_virtual_members_never_inherited_local_highlighting = suggestion +resharper_clear_attribute_is_obsolete_all_highlighting = warning +resharper_clear_attribute_is_obsolete_highlighting = warning +resharper_closure_on_modified_variable_highlighting = warning +resharper_coerced_equals_using_highlighting = warning +resharper_coerced_equals_using_with_null_undefined_highlighting = none +resharper_collection_never_queried_global_highlighting = warning +resharper_collection_never_queried_local_highlighting = warning +resharper_collection_never_updated_global_highlighting = warning +resharper_collection_never_updated_local_highlighting = warning +resharper_comma_not_valid_here_highlighting = error +resharper_comment_typo_highlighting = none +resharper_common_js_external_module_highlighting = suggestion +resharper_compare_non_constrained_generic_with_null_highlighting = none +resharper_compare_of_floats_by_equality_operator_highlighting = warning +resharper_complex_object_destructuring_problem_highlighting = warning +resharper_complex_object_in_context_destructuring_problem_highlighting = warning +resharper_conditional_access_qualifier_is_non_nullable_according_to_api_contract_highlighting = warning +resharper_conditional_ternary_equal_branch_highlighting = warning +resharper_condition_is_always_const_highlighting = warning +resharper_condition_is_always_true_or_false_according_to_nullable_api_contract_highlighting = warning +resharper_condition_is_always_true_or_false_highlighting = hint +resharper_confusing_char_as_integer_in_constructor_highlighting = warning +resharper_constant_conditional_access_qualifier_highlighting = warning +resharper_constant_null_coalescing_condition_highlighting = warning +resharper_consteval_if_is_always_constant_highlighting = warning +resharper_constructor_call_not_used_highlighting = warning +resharper_constructor_initializer_loop_highlighting = warning +resharper_container_annotation_redundancy_highlighting = warning +resharper_contextual_logger_problem_highlighting = warning +resharper_context_value_is_provided_highlighting = none +resharper_contract_annotation_not_parsed_highlighting = warning +resharper_convert_closure_to_method_group_highlighting = suggestion +resharper_convert_conditional_ternary_expression_to_switch_expression_highlighting = hint +resharper_convert_constructor_to_member_initializers_highlighting = suggestion +resharper_convert_if_do_to_while_highlighting = suggestion +resharper_convert_if_statement_to_conditional_ternary_expression_highlighting = suggestion +resharper_convert_if_statement_to_null_coalescing_assignment_highlighting = suggestion +resharper_convert_if_statement_to_null_coalescing_expression_highlighting = suggestion +resharper_convert_if_statement_to_return_statement_highlighting = hint +resharper_convert_if_statement_to_switch_statement_highlighting = hint +resharper_convert_if_to_or_expression_highlighting = suggestion +resharper_convert_nullable_to_short_form_highlighting = suggestion +resharper_convert_switch_statement_to_switch_expression_highlighting = hint +resharper_convert_to_auto_property_highlighting = suggestion +resharper_convert_to_auto_property_when_possible_highlighting = hint +resharper_convert_to_auto_property_with_private_setter_highlighting = hint +resharper_convert_to_compound_assignment_highlighting = hint +resharper_convert_to_constant_global_highlighting = hint +resharper_convert_to_constant_local_highlighting = none +resharper_convert_to_lambda_expression_highlighting = suggestion +resharper_convert_to_local_function_highlighting = suggestion +resharper_convert_to_null_coalescing_compound_assignment_highlighting = suggestion +resharper_convert_to_primary_constructor_highlighting = suggestion +resharper_convert_to_static_class_highlighting = suggestion +resharper_convert_to_using_declaration_highlighting = suggestion +resharper_convert_to_vb_auto_property_highlighting = suggestion +resharper_convert_to_vb_auto_property_when_possible_highlighting = hint +resharper_convert_to_vb_auto_property_with_private_setter_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = warning +resharper_convert_type_check_to_null_check_highlighting = warning +resharper_co_variant_array_conversion_highlighting = warning +resharper_cpp_abstract_class_without_specifier_highlighting = warning +resharper_cpp_abstract_final_class_highlighting = warning +resharper_cpp_abstract_virtual_function_call_in_ctor_highlighting = error +resharper_cpp_access_specifier_with_no_declarations_highlighting = suggestion +resharper_cpp_assigned_value_is_never_used_highlighting = warning +resharper_cpp_awaiter_type_is_not_class_highlighting = warning +resharper_cpp_bad_angle_brackets_spaces_highlighting = none +resharper_cpp_bad_braces_spaces_highlighting = none +resharper_cpp_bad_child_statement_indent_highlighting = none +resharper_cpp_bad_colon_spaces_highlighting = none +resharper_cpp_bad_comma_spaces_highlighting = none +resharper_cpp_bad_control_braces_indent_highlighting = none +resharper_cpp_bad_control_braces_line_breaks_highlighting = none +resharper_cpp_bad_declaration_braces_indent_highlighting = none +resharper_cpp_bad_declaration_braces_line_breaks_highlighting = none +resharper_cpp_bad_empty_braces_line_breaks_highlighting = none +resharper_cpp_bad_expression_braces_indent_highlighting = none +resharper_cpp_bad_expression_braces_line_breaks_highlighting = none +resharper_cpp_bad_indent_highlighting = none +resharper_cpp_bad_list_line_breaks_highlighting = none +resharper_cpp_bad_member_access_spaces_highlighting = none +resharper_cpp_bad_namespace_braces_indent_highlighting = none +resharper_cpp_bad_parens_line_breaks_highlighting = none +resharper_cpp_bad_parens_spaces_highlighting = none +resharper_cpp_bad_semicolon_spaces_highlighting = none +resharper_cpp_bad_spaces_after_keyword_highlighting = none +resharper_cpp_bad_square_brackets_spaces_highlighting = none +resharper_cpp_bad_switch_braces_indent_highlighting = none +resharper_cpp_bad_symbol_spaces_highlighting = none +resharper_cpp_boolean_increment_expression_highlighting = warning +resharper_cpp_boost_format_bad_code_highlighting = warning +resharper_cpp_boost_format_legacy_code_highlighting = suggestion +resharper_cpp_boost_format_mixed_args_highlighting = error +resharper_cpp_boost_format_too_few_args_highlighting = error +resharper_cpp_boost_format_too_many_args_highlighting = warning +resharper_cpp_clang_tidy_abseil_cleanup_ctad_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_addition_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_comparison_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_conversion_cast_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_division_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_factory_float_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_factory_scale_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_subtraction_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_unnecessary_conversion_highlighting = none +resharper_cpp_clang_tidy_abseil_faster_strsplit_delimiter_highlighting = none +resharper_cpp_clang_tidy_abseil_no_internal_dependencies_highlighting = none +resharper_cpp_clang_tidy_abseil_no_namespace_highlighting = none +resharper_cpp_clang_tidy_abseil_redundant_strcat_calls_highlighting = none +resharper_cpp_clang_tidy_abseil_string_find_startswith_highlighting = none +resharper_cpp_clang_tidy_abseil_string_find_str_contains_highlighting = none +resharper_cpp_clang_tidy_abseil_str_cat_append_highlighting = none +resharper_cpp_clang_tidy_abseil_time_comparison_highlighting = none +resharper_cpp_clang_tidy_abseil_time_subtraction_highlighting = none +resharper_cpp_clang_tidy_abseil_upgrade_duration_conversions_highlighting = none +resharper_cpp_clang_tidy_altera_id_dependent_backward_branch_highlighting = none +resharper_cpp_clang_tidy_altera_kernel_name_restriction_highlighting = none +resharper_cpp_clang_tidy_altera_single_work_item_barrier_highlighting = none +resharper_cpp_clang_tidy_altera_struct_pack_align_highlighting = none +resharper_cpp_clang_tidy_altera_unroll_loops_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_accept4_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_accept_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_creat_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_dup_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_epoll_create1_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_epoll_create_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_fopen_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_inotify_init1_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_inotify_init_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_memfd_create_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_open_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_pipe2_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_pipe_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_socket_highlighting = none +resharper_cpp_clang_tidy_android_comparison_in_temp_failure_retry_highlighting = none +resharper_cpp_clang_tidy_boost_use_to_string_highlighting = suggestion +resharper_cpp_clang_tidy_bugprone_argument_comment_highlighting = suggestion +resharper_cpp_clang_tidy_bugprone_assert_side_effect_highlighting = warning +resharper_cpp_clang_tidy_bugprone_assignment_in_if_condition_highlighting = none +resharper_cpp_clang_tidy_bugprone_bad_signal_to_kill_thread_highlighting = warning +resharper_cpp_clang_tidy_bugprone_bool_pointer_implicit_conversion_highlighting = none +resharper_cpp_clang_tidy_bugprone_branch_clone_highlighting = warning +resharper_cpp_clang_tidy_bugprone_copy_constructor_init_highlighting = warning +resharper_cpp_clang_tidy_bugprone_dangling_handle_highlighting = warning +resharper_cpp_clang_tidy_bugprone_dynamic_static_initializers_highlighting = warning +resharper_cpp_clang_tidy_bugprone_easily_swappable_parameters_highlighting = none +resharper_cpp_clang_tidy_bugprone_exception_escape_highlighting = none +resharper_cpp_clang_tidy_bugprone_fold_init_type_highlighting = warning +resharper_cpp_clang_tidy_bugprone_forwarding_reference_overload_highlighting = warning +resharper_cpp_clang_tidy_bugprone_forward_declaration_namespace_highlighting = warning +resharper_cpp_clang_tidy_bugprone_implicit_widening_of_multiplication_result_highlighting = warning +resharper_cpp_clang_tidy_bugprone_inaccurate_erase_highlighting = warning +resharper_cpp_clang_tidy_bugprone_incorrect_roundings_highlighting = warning +resharper_cpp_clang_tidy_bugprone_infinite_loop_highlighting = warning +resharper_cpp_clang_tidy_bugprone_integer_division_highlighting = warning +resharper_cpp_clang_tidy_bugprone_lambda_function_name_highlighting = warning +resharper_cpp_clang_tidy_bugprone_macro_parentheses_highlighting = warning +resharper_cpp_clang_tidy_bugprone_macro_repeated_side_effects_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_operator_in_strlen_in_alloc_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_pointer_arithmetic_in_alloc_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_widening_cast_highlighting = warning +resharper_cpp_clang_tidy_bugprone_move_forwarding_reference_highlighting = warning +resharper_cpp_clang_tidy_bugprone_multiple_statement_macro_highlighting = warning +resharper_cpp_clang_tidy_bugprone_narrowing_conversions_highlighting = warning +resharper_cpp_clang_tidy_bugprone_not_null_terminated_result_highlighting = warning +resharper_cpp_clang_tidy_bugprone_no_escape_highlighting = warning +resharper_cpp_clang_tidy_bugprone_parent_virtual_call_highlighting = warning +resharper_cpp_clang_tidy_bugprone_posix_return_highlighting = warning +resharper_cpp_clang_tidy_bugprone_redundant_branch_condition_highlighting = warning +resharper_cpp_clang_tidy_bugprone_reserved_identifier_highlighting = warning +resharper_cpp_clang_tidy_bugprone_shared_ptr_array_mismatch_highlighting = warning +resharper_cpp_clang_tidy_bugprone_signal_handler_highlighting = warning +resharper_cpp_clang_tidy_bugprone_signed_char_misuse_highlighting = warning +resharper_cpp_clang_tidy_bugprone_sizeof_container_highlighting = warning +resharper_cpp_clang_tidy_bugprone_sizeof_expression_highlighting = warning +resharper_cpp_clang_tidy_bugprone_spuriously_wake_up_functions_highlighting = warning +resharper_cpp_clang_tidy_bugprone_standalone_empty_highlighting = warning +resharper_cpp_clang_tidy_bugprone_stringview_nullptr_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_constructor_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_integer_assignment_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_literal_with_embedded_nul_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_enum_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_include_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_memory_comparison_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_memset_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_missing_comma_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_realloc_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_semicolon_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_string_compare_highlighting = warning +resharper_cpp_clang_tidy_bugprone_swapped_arguments_highlighting = warning +resharper_cpp_clang_tidy_bugprone_terminating_continue_highlighting = warning +resharper_cpp_clang_tidy_bugprone_throw_keyword_missing_highlighting = warning +resharper_cpp_clang_tidy_bugprone_too_small_loop_variable_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unchecked_optional_access_highlighting = warning +resharper_cpp_clang_tidy_bugprone_undefined_memory_manipulation_highlighting = warning +resharper_cpp_clang_tidy_bugprone_undelegated_constructor_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unhandled_exception_at_new_highlighting = none +resharper_cpp_clang_tidy_bugprone_unhandled_self_assignment_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unused_raii_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unused_return_value_highlighting = warning +resharper_cpp_clang_tidy_bugprone_use_after_move_highlighting = warning +resharper_cpp_clang_tidy_bugprone_virtual_near_miss_highlighting = suggestion +resharper_cpp_clang_tidy_cert_con36_c_highlighting = none +resharper_cpp_clang_tidy_cert_con54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl03_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl16_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl21_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl37_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl50_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl51_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl58_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_dcl59_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_env33_c_highlighting = none +resharper_cpp_clang_tidy_cert_err09_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err33_c_highlighting = warning +resharper_cpp_clang_tidy_cert_err34_c_highlighting = suggestion +resharper_cpp_clang_tidy_cert_err52_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err58_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err60_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_err61_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_exp42_c_highlighting = none +resharper_cpp_clang_tidy_cert_fio38_c_highlighting = none +resharper_cpp_clang_tidy_cert_flp30_c_highlighting = warning +resharper_cpp_clang_tidy_cert_flp37_c_highlighting = none +resharper_cpp_clang_tidy_cert_mem57_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_msc30_c_highlighting = none +resharper_cpp_clang_tidy_cert_msc32_c_highlighting = none +resharper_cpp_clang_tidy_cert_msc50_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_msc51_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_msc54_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_oop11_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_oop54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_oop57_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_oop58_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_pos44_c_highlighting = none +resharper_cpp_clang_tidy_cert_pos47_c_highlighting = none +resharper_cpp_clang_tidy_cert_sig30_c_highlighting = none +resharper_cpp_clang_tidy_cert_str34_c_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_errno_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_google_g_test_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_cast_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_return_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_std_c_library_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_trust_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_trust_returns_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_builtin_builtin_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_builtin_no_return_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_divide_zero_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_dynamic_type_propagation_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_nonnil_string_constants_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_non_null_param_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_null_dereference_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_stack_address_escape_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_stack_addr_escape_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_undefined_binary_operator_result_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_array_subscript_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_assign_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_branch_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_captured_block_variable_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_new_array_size_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_undef_return_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_vla_size_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_inner_pointer_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_move_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_leaks_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_placement_new_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_pure_virtual_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_self_assignment_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_smart_ptr_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_string_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_virtual_call_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_deadcode_dead_stores_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_fuchsia_handle_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullability_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_dereferenced_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_passed_to_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_returned_from_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_null_passed_to_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_null_returned_from_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_uninitialized_object_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_virtual_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_mpi_mpi_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_empty_localization_context_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_non_localized_string_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_os_object_c_style_cast_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_performance_gcd_antipattern_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_performance_padding_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_portability_unix_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_at_sync_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_autorelease_write_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_class_release_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_dealloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_incompatible_method_types_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_loops_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_missing_super_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_nil_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_non_nil_return_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_autorelease_pool_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_error_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_obj_c_generics_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_run_loop_autorelease_leak_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_self_init_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_super_dealloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_unused_ivars_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_variadic_method_types_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_error_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_number_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_retain_release_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_out_of_bounds_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_pointer_sized_values_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_mig_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_ns_or_cf_error_deref_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_number_object_conversion_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_obj_c_property_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_os_object_retain_count_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_sec_keychain_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_float_loop_counter_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcmp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcopy_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bzero_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_decode_value_of_obj_c_type_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_deprecated_or_unsafe_buffer_handling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_getpw_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_gets_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mkstemp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mktemp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_rand_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_security_syntax_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_strcpy_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_unchecked_return_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_vfork_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_bad_size_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_c_string_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_null_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_dynamic_memory_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_malloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_malloc_sizeof_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_mismatched_deallocator_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_vfork_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_copy_to_self_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_uninitialized_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_unterminated_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_valist_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_no_uncounted_member_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_ref_cntbl_base_virtual_dtor_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_uncounted_lambda_captures_checker_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_absolute_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_abstract_final_class_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_abstract_vbase_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_address_of_packed_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_address_of_temporary_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_aix_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_align_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_alloca_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_alloca_with_align_alignof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_always_inline_coroutine_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_ellipsis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_member_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_reversed_operator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_analyzer_incompatible_plugin_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_anonymous_pack_parens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_anon_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_bridge_casts_disallowed_in_nonarc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_maybe_repeated_use_of_weak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_non_pod_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_perform_selector_leaks_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_repeated_use_of_weak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_retain_cycles_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_unsafe_retained_assign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_argument_outside_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_argument_undefined_behaviour_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_bounds_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_bounds_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_asm_operand_widths_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_assign_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_assume_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atimport_in_framework_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_alignment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_implicit_seq_cst_highlighting = suggestion +resharper_cpp_clang_tidy_clang_diagnostic_atomic_memory_ordering_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_property_with_user_defined_accessor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_attribute_packed_for_bitfield_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_attribute_warning_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_at_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_disable_vptr_sanitizer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_import_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_storage_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_var_id_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_availability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_avr_rtlib_linking_quirks_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_backslash_newline_escape_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bad_function_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_binding_in_condition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bind_to_temporary_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_width_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_conditional_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_instead_of_logical_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bit_int_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_block_capture_autoreleasing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bool_operation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_braced_scalar_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_branch_protection_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bridge_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_assume_aligned_alignment_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_builtin_macro_redefined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_memcpy_chk_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_requires_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c11_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c2x_compat_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c2x_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c99_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_c99_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c99_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_called_once_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_call_to_pure_virtual_from_ctor_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_align_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_calling_convention_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_function_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_function_type_strict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_of_sel_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_qual_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_qual_unrelated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cf_string_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_char_subscripts_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_clang_cl_pch_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_class_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_class_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cmse_union_leak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_comma_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_comment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compare_distinct_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_completion_handler_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_complex_component_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_space_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conditional_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conditional_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_config_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_evaluated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_logical_operand_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constexpr_not_const_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_consumed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_coroutine_missing_unhandled_exception_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_coro_non_aligned_allocation_funciton_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_covered_switch_default_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_deprecated_writable_strings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_reserved_user_defined_literal_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_extra_semi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_inline_namespace_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_long_long_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_narrowing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_binary_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_mangling_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp2a_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp2a_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp2b_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_bind_to_temporary_copy_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_extra_semi_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_local_type_template_args_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_unnamed_type_template_args_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_binary_literal_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cstring_format_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ctad_maybe_unsupported_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ctu_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cuda_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_custom_atomic_properties_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cxx_attribute_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_else_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_gsl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_initializer_list_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_darwin_sdk_settings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_date_time_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dealloc_in_category_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_debug_compression_unavailable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_declaration_after_statement_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_defaulted_function_deleted_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_delegating_ctor_cycles_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_abstract_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_incomplete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_non_abstract_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delimited_escape_sequence_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_altivec_src_compat_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_anon_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_array_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_attributes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_builtins_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_comma_subscript_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_user_provided_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_user_provided_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_coroutine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_dynamic_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_conditional_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_experimental_coroutine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_implementations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_increment_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_module_ts_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_non_prototype_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_isa_usage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_perform_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_pragma_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_register_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_static_analyzer_flag_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_this_capture_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_volatile_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecate_lax_vec_conv_all_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_direct_ivar_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_disabled_macro_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_distributed_object_modifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_division_by_zero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dllexport_explicit_instantiation_decl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dllimport_static_field_def_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dll_attribute_on_redeclaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_documentation_deprecated_sync_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_html_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_unknown_command_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_dollar_in_identifier_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_double_promotion_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_dtor_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dtor_typedef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_decl_specifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_arg_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_match_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dynamic_class_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dynamic_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_embedded_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_body_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_decomposition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_init_stmt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_translation_unit_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_encode_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_conditional_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_switch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_constexpr_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_too_large_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_error_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_exceptions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_excess_initializers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_exit_time_destructors_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_expansion_to_defined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_explicit_initialize_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_explicit_ownership_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_export_unnamed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_export_using_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extern_c_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_extern_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_qualification_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_semi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_semi_stmt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_tokens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_final_dtor_non_final_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_final_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_fixed_enum_extension_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_fixed_point_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_flag_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_flexible_array_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_equal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_overflow_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_zero_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_extra_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_insufficient_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_invalid_specifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_nonliteral_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_non_iso_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_security_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_type_confusion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_zero_length_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_fortify_source_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_for_loop_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_four_char_constants_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_framework_include_private_from_public_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_frame_address_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_frame_larger_than_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_free_nonheap_object_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_function_def_in_objc_container_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_function_multiversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_future_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gcc_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_global_constructors_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_global_isel_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_alignof_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_anonymous_struct_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_gnu_array_member_paren_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_auto_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_binary_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_case_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_complex_integer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_compound_literal_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_conditional_omitted_operand_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_struct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_union_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_folding_constant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_imaginary_constant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_include_next_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_inline_cpp_without_extern_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_label_as_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_line_marker_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_null_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_offsetof_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_pointer_arith_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_redeclared_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_statement_expression_from_macro_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_statement_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_static_float_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_string_literal_operator_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_union_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_variable_sized_type_not_at_end_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_zero_variadic_macro_arguments_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gpu_maybe_wrong_side_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_header_guard_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_header_hygiene_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_hip_only_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_hlsl_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_idiomatic_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_attributes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_availability_without_sdk_settings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_optimization_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragmas_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_intrinsic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_optimize_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_qualifiers_highlighting = suggestion +resharper_cpp_clang_tidy_clang_diagnostic_ignored_reference_qualifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicitly_unsigned_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_atomic_properties_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_const_int_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_conversion_floating_point_to_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_exception_spec_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_per_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fixed_point_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_function_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_retain_self_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_import_preprocessor_directive_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inaccessible_base_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_include_next_absolute_path_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_include_next_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_function_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_function_pointer_types_strict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_library_redeclaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_ms_struct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_discards_qualifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_property_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_sysroot_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_framework_module_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_implementation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_setjmp_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_umbrella_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_dllimport_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_destructor_override_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_override_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_increment_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_independent_class_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_infinite_recursion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_initializer_overrides_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_injected_class_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_asm_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_namespace_reopened_noninline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_new_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_instantiation_after_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_integer_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_interrupt_service_routine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_in_bool_context_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_to_pointer_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_to_void_pointer_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_constexpr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_iboutlet_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_initializer_from_system_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_ios_deployment_target_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_noreturn_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_no_builtin_names_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_offsetof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_or_nonexistent_directory_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_partial_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_pp_token_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_source_encoding_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_token_paste_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_utf8_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_jump_seh_finally_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_keyword_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_keyword_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_knr_promoted_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_language_extension_token_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_large_by_value_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_linker_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_literal_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_literal_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_local_type_template_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_logical_not_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_logical_op_parentheses_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_long_long_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_macro_redefined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_main_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_main_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_malformed_warning_check_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_many_braces_around_scalar_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mathematical_notation_identifier_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_max_tokens_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_max_unsigned_zero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_memset_transposed_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_memsize_comparison_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_method_signatures_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_abstract_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_anon_tag_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_charize_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_comment_paste_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_const_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_cpp_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_default_arg_redefinition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_drectve_section_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_end_of_file_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_forward_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_exists_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_explicit_constructor_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_extra_qualification_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_fixed_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_flexible_array_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_goto_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_inaccessible_base_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_include_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_mutable_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_pure_definition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_redeclare_static_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_sealed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_static_assert_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_shadow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_union_member_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_unqualified_friend_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_using_decl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_void_pseudo_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_misexpect_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_misleading_indentation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_new_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_parameter_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_return_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_tags_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_braces_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_constinit_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_field_initializers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_method_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_noescape_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_noreturn_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_prototypes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_missing_prototype_for_cc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_selector_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_sysroot_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_variable_declarations_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_misspelled_assumption_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_modules_ambiguous_internal_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_modules_import_nested_redundant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_conflict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_file_config_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_file_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_import_in_extern_c_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_msvc_not_found_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_multichar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_multiple_move_vbase_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nested_anon_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_newline_eof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_new_returns_null_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_noderef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nonnull_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_include_path_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_system_include_path_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_vector_initialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nontrivial_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_c_typedef_for_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_literal_null_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_framework_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_pod_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_power_of_two_alignment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nsconsumed_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nsreturns_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ns_object_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_on_arrays_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_declspec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_inferred_on_nested_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullable_to_nonnull_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_character_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_dereference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_pointer_subtraction_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_odr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_old_style_cast_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_opencl_unsupported_rgba_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp51_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_clauses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_loop_form_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_mapping_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_target_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_option_ignored_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ordered_compare_function_pointers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_out_of_line_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_out_of_scope_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overlength_strings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overloaded_shift_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overloaded_virtual_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_override_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_override_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overriding_method_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overriding_t_option_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_over_aligned_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_packed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_packed_non_pod_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_padded_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_parentheses_equality_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pass_failed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pch_date_time_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_core_features_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pessimizing_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_arith_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_integer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_sign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_to_enum_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_to_int_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_poison_system_directories_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_potentially_evaluated_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragmas_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_clang_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_messages_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_once_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_suspicious_include_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_system_header_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_predefined_identifier_outside_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pre_c2x_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_c2x_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp20_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp20_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp2b_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp2b_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_openmp51_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_private_extern_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_private_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_private_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_missing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_out_of_date_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_unprofiled_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_property_access_dot_syntax_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_property_attribute_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_protocol_property_synthesis_ambiguity_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_psabi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_qualified_void_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_quoted_include_in_framework_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_bind_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_construct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_readonly_iboutlet_property_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_read_only_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_receiver_expr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_receiver_forward_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redeclared_class_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_consteval_if_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_parens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_register_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reinterpret_base_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_ctor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_init_list_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_requires_super_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_identifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_id_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_macro_identifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_user_defined_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_restrict_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_retained_language_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_local_addr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_stack_address_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_std_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_type_c_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_type_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_rewrite_not_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sarif_format_unstable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_section_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_selector_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_overloaded_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_semicolon_before_method_body_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sentinel_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_serialized_diagnostics_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_modified_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shadow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_ivar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_uncaptured_local_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shift_count_negative_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_count_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_negative_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_sign_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shorten64_to32_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_signed_enum_bitfield_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_signed_unsigned_wchar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sign_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sign_conversion_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_single_bit_bitfield_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_decay_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_div_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_div_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_slash_u_filename_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_slh_asm_goto_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_sometimes_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_source_uses_openmp_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_spirv_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_spir_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_static_float_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_inline_explicit_instantiation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_in_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_local_in_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_self_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_stdlibcxx_not_found_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_strict_prototypes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strict_selector_match_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_concatenation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_plus_char_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_plus_int_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strlcpy_strlcat_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strncat_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_suggest_destructor_override_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_suggest_override_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_super_class_method_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_suspicious_bzero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sync_fetch_and_nand_semantics_changed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_target_clones_mixed_specifiers_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_tautological_bitwise_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_in_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_out_of_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_objc_bool_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_overlap_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_pointer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_type_limit_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_undefined_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_char_zero_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_enum_zero_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_zero_compare_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_tautological_value_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tentative_definition_incomplete_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_attributes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_beta_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_negative_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_precise_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_verbose_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_trigraphs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_typedef_redefinition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_typename_missing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_type_safety_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unable_to_open_stats_file_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unaligned_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unaligned_qualifier_implicit_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unavailable_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undeclared_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_func_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_reinterpret_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_var_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undef_prefix_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_underaligned_exception_object_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unevaluated_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_new_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_homoglyph_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_whitespace_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_zero_width_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_uninitialized_const_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_attributes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_cuda_version_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_directives_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_escape_sequence_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_pragmas_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_sanitizers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_warning_option_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unnamed_type_template_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unneeded_internal_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unneeded_member_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unqualified_std_cast_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_break_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_fallthrough_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_generic_assoc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_loop_increment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_return_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsafe_buffer_usage_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unsequenced_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_abi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_abs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_availability_guard_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_cb_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_dll_base_class_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_friend_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_gpopt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_nan_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_target_opt_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_visibility_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unusable_partial_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_but_set_parameter_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_but_set_variable_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_comparison_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_const_variable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_exception_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_getter_return_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_label_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_lambda_capture_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_local_typedef_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_member_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_parameter_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_private_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_property_ivar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_result_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_template_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_variable_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_volatile_lvalue_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_used_but_marked_unused_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_user_defined_literals_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_user_defined_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_variadic_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vector_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vec_elem_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vexing_parse_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_visibility_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vla_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vla_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_enum_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_int_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_ptr_dereference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_wasm_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_weak_template_vtables_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_weak_vtables_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_writable_strings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_xor_used_as_pow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_zero_as_null_pointer_constant_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_zero_length_array_highlighting = warning +resharper_cpp_clang_tidy_concurrency_mt_unsafe_highlighting = warning +resharper_cpp_clang_tidy_concurrency_thread_canceltype_asynchronous_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_const_or_ref_data_members_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_do_while_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_goto_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_magic_numbers_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_non_const_global_variables_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_reference_coroutine_parameters_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_c_copy_assignment_signature_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_explicit_virtual_functions_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_init_variables_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_interfaces_global_init_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_macro_usage_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_narrowing_conversions_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_non_private_member_variables_in_classes_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_no_malloc_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_owning_memory_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_prefer_member_initializer_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_array_to_pointer_decay_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_constant_array_index_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_pointer_arithmetic_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_const_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_cstyle_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_member_init_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_reinterpret_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_static_cast_downcast_highlighting = suggestion +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_union_access_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_vararg_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_slicing_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_special_member_functions_highlighting = suggestion +resharper_cpp_clang_tidy_cppcoreguidelines_virtual_class_destructor_highlighting = none +resharper_cpp_clang_tidy_darwin_avoid_spinlock_highlighting = none +resharper_cpp_clang_tidy_darwin_dispatch_once_nonstatic_highlighting = none +resharper_cpp_clang_tidy_fuchsia_default_arguments_calls_highlighting = none +resharper_cpp_clang_tidy_fuchsia_default_arguments_declarations_highlighting = none +resharper_cpp_clang_tidy_fuchsia_header_anon_namespaces_highlighting = none +resharper_cpp_clang_tidy_fuchsia_multiple_inheritance_highlighting = none +resharper_cpp_clang_tidy_fuchsia_overloaded_operator_highlighting = none +resharper_cpp_clang_tidy_fuchsia_statically_constructed_objects_highlighting = none +resharper_cpp_clang_tidy_fuchsia_trailing_return_highlighting = none +resharper_cpp_clang_tidy_fuchsia_virtual_inheritance_highlighting = none +resharper_cpp_clang_tidy_google_build_explicit_make_pair_highlighting = none +resharper_cpp_clang_tidy_google_build_namespaces_highlighting = none +resharper_cpp_clang_tidy_google_build_using_namespace_highlighting = none +resharper_cpp_clang_tidy_google_default_arguments_highlighting = none +resharper_cpp_clang_tidy_google_explicit_constructor_highlighting = none +resharper_cpp_clang_tidy_google_global_names_in_headers_highlighting = none +resharper_cpp_clang_tidy_google_objc_avoid_nsobject_new_highlighting = none +resharper_cpp_clang_tidy_google_objc_avoid_throwing_exception_highlighting = none +resharper_cpp_clang_tidy_google_objc_function_naming_highlighting = none +resharper_cpp_clang_tidy_google_objc_global_variable_declaration_highlighting = none +resharper_cpp_clang_tidy_google_readability_avoid_underscore_in_googletest_name_highlighting = none +resharper_cpp_clang_tidy_google_readability_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_google_readability_casting_highlighting = none +resharper_cpp_clang_tidy_google_readability_function_size_highlighting = none +resharper_cpp_clang_tidy_google_readability_namespace_comments_highlighting = none +resharper_cpp_clang_tidy_google_readability_todo_highlighting = none +resharper_cpp_clang_tidy_google_runtime_int_highlighting = none +resharper_cpp_clang_tidy_google_runtime_operator_highlighting = warning +resharper_cpp_clang_tidy_google_upgrade_googletest_case_highlighting = suggestion +resharper_cpp_clang_tidy_hicpp_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_hicpp_avoid_goto_highlighting = warning +resharper_cpp_clang_tidy_hicpp_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_hicpp_deprecated_headers_highlighting = none +resharper_cpp_clang_tidy_hicpp_exception_baseclass_highlighting = suggestion +resharper_cpp_clang_tidy_hicpp_explicit_conversions_highlighting = none +resharper_cpp_clang_tidy_hicpp_function_size_highlighting = none +resharper_cpp_clang_tidy_hicpp_invalid_access_moved_highlighting = none +resharper_cpp_clang_tidy_hicpp_member_init_highlighting = none +resharper_cpp_clang_tidy_hicpp_move_const_arg_highlighting = none +resharper_cpp_clang_tidy_hicpp_multiway_paths_covered_highlighting = warning +resharper_cpp_clang_tidy_hicpp_named_parameter_highlighting = none +resharper_cpp_clang_tidy_hicpp_new_delete_operators_highlighting = none +resharper_cpp_clang_tidy_hicpp_noexcept_move_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_array_decay_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_assembler_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_malloc_highlighting = none +resharper_cpp_clang_tidy_hicpp_signed_bitwise_highlighting = none +resharper_cpp_clang_tidy_hicpp_special_member_functions_highlighting = none +resharper_cpp_clang_tidy_hicpp_static_assert_highlighting = none +resharper_cpp_clang_tidy_hicpp_undelegated_constructor_highlighting = none +resharper_cpp_clang_tidy_hicpp_uppercase_literal_suffix_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_auto_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_emplace_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_equals_default_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_equals_delete_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_noexcept_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_nullptr_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_override_highlighting = none +resharper_cpp_clang_tidy_hicpp_vararg_highlighting = none +resharper_cpp_clang_tidy_highlighting_highlighting = suggestion +resharper_cpp_clang_tidy_linuxkernel_must_check_errs_highlighting = warning +resharper_cpp_clang_tidy_llvmlibc_callee_namespace_highlighting = none +resharper_cpp_clang_tidy_llvmlibc_implementation_in_namespace_highlighting = none +resharper_cpp_clang_tidy_llvmlibc_restrict_system_libc_headers_highlighting = none +resharper_cpp_clang_tidy_llvm_else_after_return_highlighting = none +resharper_cpp_clang_tidy_llvm_header_guard_highlighting = none +resharper_cpp_clang_tidy_llvm_include_order_highlighting = none +resharper_cpp_clang_tidy_llvm_namespace_comment_highlighting = none +resharper_cpp_clang_tidy_llvm_prefer_isa_or_dyn_cast_in_conditionals_highlighting = none +resharper_cpp_clang_tidy_llvm_prefer_register_over_unsigned_highlighting = suggestion +resharper_cpp_clang_tidy_llvm_qualified_auto_highlighting = none +resharper_cpp_clang_tidy_llvm_twine_local_highlighting = none +resharper_cpp_clang_tidy_misc_confusable_identifiers_highlighting = warning +resharper_cpp_clang_tidy_misc_const_correctness_highlighting = none +resharper_cpp_clang_tidy_misc_definitions_in_headers_highlighting = none +resharper_cpp_clang_tidy_misc_misleading_bidirectional_highlighting = warning +resharper_cpp_clang_tidy_misc_misleading_identifier_highlighting = warning +resharper_cpp_clang_tidy_misc_misplaced_const_highlighting = none +resharper_cpp_clang_tidy_misc_new_delete_overloads_highlighting = warning +resharper_cpp_clang_tidy_misc_non_copyable_objects_highlighting = warning +resharper_cpp_clang_tidy_misc_non_private_member_variables_in_classes_highlighting = none +resharper_cpp_clang_tidy_misc_no_recursion_highlighting = none +resharper_cpp_clang_tidy_misc_redundant_expression_highlighting = warning +resharper_cpp_clang_tidy_misc_static_assert_highlighting = suggestion +resharper_cpp_clang_tidy_misc_throw_by_value_catch_by_reference_highlighting = warning +resharper_cpp_clang_tidy_misc_unconventional_assign_operator_highlighting = warning +resharper_cpp_clang_tidy_misc_uniqueptr_reset_release_highlighting = suggestion +resharper_cpp_clang_tidy_misc_unused_alias_decls_highlighting = suggestion +resharper_cpp_clang_tidy_misc_unused_parameters_highlighting = none +resharper_cpp_clang_tidy_misc_unused_using_decls_highlighting = suggestion +resharper_cpp_clang_tidy_misc_use_anonymous_namespace_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_avoid_bind_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_modernize_concat_nested_namespaces_highlighting = none +resharper_cpp_clang_tidy_modernize_deprecated_headers_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_deprecated_ios_base_aliases_highlighting = warning +resharper_cpp_clang_tidy_modernize_loop_convert_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_macro_to_enum_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_make_shared_highlighting = none +resharper_cpp_clang_tidy_modernize_make_unique_highlighting = none +resharper_cpp_clang_tidy_modernize_pass_by_value_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_raw_string_literal_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_redundant_void_arg_highlighting = none +resharper_cpp_clang_tidy_modernize_replace_auto_ptr_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_replace_disallow_copy_and_assign_macro_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_replace_random_shuffle_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_return_braced_init_list_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_shrink_to_fit_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_unary_static_assert_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_auto_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_bool_literals_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_default_member_init_highlighting = none +resharper_cpp_clang_tidy_modernize_use_emplace_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_equals_default_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_equals_delete_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_nodiscard_highlighting = hint +resharper_cpp_clang_tidy_modernize_use_noexcept_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_nullptr_highlighting = none +resharper_cpp_clang_tidy_modernize_use_override_highlighting = none +resharper_cpp_clang_tidy_modernize_use_trailing_return_type_highlighting = none +resharper_cpp_clang_tidy_modernize_use_transparent_functors_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_uncaught_exceptions_highlighting = warning +resharper_cpp_clang_tidy_modernize_use_using_highlighting = none +resharper_cpp_clang_tidy_mpi_buffer_deref_highlighting = warning +resharper_cpp_clang_tidy_mpi_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_objc_assert_equals_highlighting = warning +resharper_cpp_clang_tidy_objc_avoid_nserror_init_highlighting = warning +resharper_cpp_clang_tidy_objc_dealloc_in_category_highlighting = warning +resharper_cpp_clang_tidy_objc_forbidden_subclassing_highlighting = warning +resharper_cpp_clang_tidy_objc_missing_hash_highlighting = warning +resharper_cpp_clang_tidy_objc_nsdate_formatter_highlighting = none +resharper_cpp_clang_tidy_objc_nsinvocation_argument_lifetime_highlighting = warning +resharper_cpp_clang_tidy_objc_property_declaration_highlighting = warning +resharper_cpp_clang_tidy_objc_super_self_highlighting = warning +resharper_cpp_clang_tidy_openmp_exception_escape_highlighting = warning +resharper_cpp_clang_tidy_openmp_use_default_none_highlighting = warning +resharper_cpp_clang_tidy_performance_faster_string_find_highlighting = suggestion +resharper_cpp_clang_tidy_performance_for_range_copy_highlighting = suggestion +resharper_cpp_clang_tidy_performance_implicit_conversion_in_loop_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_algorithm_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_string_concatenation_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_vector_operation_highlighting = suggestion +resharper_cpp_clang_tidy_performance_move_constructor_init_highlighting = warning +resharper_cpp_clang_tidy_performance_move_const_arg_highlighting = suggestion +resharper_cpp_clang_tidy_performance_noexcept_move_constructor_highlighting = none +resharper_cpp_clang_tidy_performance_no_automatic_move_highlighting = warning +resharper_cpp_clang_tidy_performance_no_int_to_ptr_highlighting = warning +resharper_cpp_clang_tidy_performance_trivially_destructible_highlighting = suggestion +resharper_cpp_clang_tidy_performance_type_promotion_in_math_fn_highlighting = suggestion +resharper_cpp_clang_tidy_performance_unnecessary_copy_initialization_highlighting = suggestion +resharper_cpp_clang_tidy_performance_unnecessary_value_param_highlighting = suggestion +resharper_cpp_clang_tidy_portability_restrict_system_includes_highlighting = none +resharper_cpp_clang_tidy_portability_simd_intrinsics_highlighting = none +resharper_cpp_clang_tidy_portability_std_allocator_const_highlighting = warning +resharper_cpp_clang_tidy_readability_avoid_const_params_in_decls_highlighting = none +resharper_cpp_clang_tidy_readability_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_readability_const_return_type_highlighting = none +resharper_cpp_clang_tidy_readability_container_contains_highlighting = none +resharper_cpp_clang_tidy_readability_container_data_pointer_highlighting = suggestion +resharper_cpp_clang_tidy_readability_container_size_empty_highlighting = suggestion +resharper_cpp_clang_tidy_readability_convert_member_functions_to_static_highlighting = none +resharper_cpp_clang_tidy_readability_delete_null_pointer_highlighting = suggestion +resharper_cpp_clang_tidy_readability_duplicate_include_highlighting = none +resharper_cpp_clang_tidy_readability_else_after_return_highlighting = none +resharper_cpp_clang_tidy_readability_function_cognitive_complexity_highlighting = none +resharper_cpp_clang_tidy_readability_function_size_highlighting = none +resharper_cpp_clang_tidy_readability_identifier_length_highlighting = none +resharper_cpp_clang_tidy_readability_identifier_naming_highlighting = none +resharper_cpp_clang_tidy_readability_implicit_bool_conversion_highlighting = none +resharper_cpp_clang_tidy_readability_inconsistent_declaration_parameter_name_highlighting = suggestion +resharper_cpp_clang_tidy_readability_isolate_declaration_highlighting = none +resharper_cpp_clang_tidy_readability_magic_numbers_highlighting = none +resharper_cpp_clang_tidy_readability_make_member_function_const_highlighting = none +resharper_cpp_clang_tidy_readability_misleading_indentation_highlighting = none +resharper_cpp_clang_tidy_readability_misplaced_array_index_highlighting = suggestion +resharper_cpp_clang_tidy_readability_named_parameter_highlighting = none +resharper_cpp_clang_tidy_readability_non_const_parameter_highlighting = none +resharper_cpp_clang_tidy_readability_qualified_auto_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_access_specifiers_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_control_flow_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_declaration_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_function_ptr_dereference_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_member_init_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_preprocessor_highlighting = warning +resharper_cpp_clang_tidy_readability_redundant_smartptr_get_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_string_cstr_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_string_init_highlighting = suggestion +resharper_cpp_clang_tidy_readability_simplify_boolean_expr_highlighting = none +resharper_cpp_clang_tidy_readability_simplify_subscript_expr_highlighting = warning +resharper_cpp_clang_tidy_readability_static_accessed_through_instance_highlighting = suggestion +resharper_cpp_clang_tidy_readability_static_definition_in_anonymous_namespace_highlighting = none +resharper_cpp_clang_tidy_readability_string_compare_highlighting = warning +resharper_cpp_clang_tidy_readability_suspicious_call_argument_highlighting = warning +resharper_cpp_clang_tidy_readability_uniqueptr_delete_release_highlighting = suggestion +resharper_cpp_clang_tidy_readability_uppercase_literal_suffix_highlighting = none +resharper_cpp_clang_tidy_readability_use_anyofallof_highlighting = suggestion +resharper_cpp_clang_tidy_zircon_temporary_objects_highlighting = none +resharper_cpp_class_can_be_final_highlighting = hint +resharper_cpp_class_is_incomplete_highlighting = warning +resharper_cpp_class_needs_constructor_because_of_uninitialized_member_highlighting = warning +resharper_cpp_class_never_used_highlighting = warning +resharper_cpp_compile_time_constant_can_be_replaced_with_boolean_constant_highlighting = suggestion +resharper_cpp_concept_never_used_highlighting = warning +resharper_cpp_const_parameter_in_declaration_highlighting = suggestion +resharper_cpp_const_value_function_return_type_highlighting = suggestion +resharper_cpp_coroutine_call_resolve_error_highlighting = warning +resharper_cpp_cv_qualifier_can_not_be_applied_to_reference_highlighting = warning +resharper_cpp_c_style_cast_highlighting = suggestion +resharper_cpp_declaration_hides_local_highlighting = warning +resharper_cpp_declaration_hides_uncaptured_local_highlighting = hint +resharper_cpp_declaration_specifier_without_declarators_highlighting = warning +resharper_cpp_declarator_disambiguated_as_function_highlighting = warning +resharper_cpp_declarator_never_used_highlighting = warning +resharper_cpp_declarator_used_before_initialization_highlighting = error +resharper_cpp_defaulted_special_member_function_is_implicitly_deleted_highlighting = warning +resharper_cpp_default_case_not_handled_in_switch_statement_highlighting = warning +resharper_cpp_default_initialization_with_no_user_constructor_highlighting = warning +resharper_cpp_default_is_used_as_identifier_highlighting = warning +resharper_cpp_deleting_void_pointer_highlighting = warning +resharper_cpp_dependent_template_without_template_keyword_highlighting = warning +resharper_cpp_dependent_type_without_typename_keyword_highlighting = warning +resharper_cpp_deprecated_entity_highlighting = warning +resharper_cpp_deprecated_register_storage_class_specifier_highlighting = warning +resharper_cpp_dereference_operator_limit_exceeded_highlighting = warning +resharper_cpp_discarded_postfix_operator_result_highlighting = suggestion +resharper_cpp_doxygen_syntax_error_highlighting = warning +resharper_cpp_doxygen_undocumented_parameter_highlighting = suggestion +resharper_cpp_doxygen_unresolved_reference_highlighting = warning +resharper_cpp_empty_declaration_highlighting = warning +resharper_cpp_enforce_cv_qualifiers_order_highlighting = none +resharper_cpp_enforce_cv_qualifiers_placement_highlighting = none +resharper_cpp_enforce_do_statement_braces_highlighting = none +resharper_cpp_enforce_for_statement_braces_highlighting = none +resharper_cpp_enforce_function_declaration_style_highlighting = none +resharper_cpp_enforce_if_statement_braces_highlighting = none +resharper_cpp_enforce_nested_namespaces_style_highlighting = hint +resharper_cpp_enforce_overriding_destructor_style_highlighting = suggestion +resharper_cpp_enforce_overriding_function_style_highlighting = suggestion +resharper_cpp_enforce_type_alias_code_style_highlighting = none +resharper_cpp_enforce_while_statement_braces_highlighting = none +resharper_cpp_entity_assigned_but_no_read_highlighting = warning +resharper_cpp_entity_used_only_in_unevaluated_context_highlighting = warning +resharper_cpp_enumerator_never_used_highlighting = warning +resharper_cpp_equal_operands_in_binary_expression_highlighting = warning +resharper_cpp_explicit_specialization_in_non_namespace_scope_highlighting = warning +resharper_cpp_expression_without_side_effects_highlighting = warning +resharper_cpp_final_function_in_final_class_highlighting = suggestion +resharper_cpp_final_non_overriding_virtual_function_highlighting = suggestion +resharper_cpp_forward_enum_declaration_without_underlying_type_highlighting = warning +resharper_cpp_for_loop_can_be_replaced_with_while_highlighting = suggestion +resharper_cpp_functional_style_cast_highlighting = suggestion +resharper_cpp_function_doesnt_return_value_highlighting = warning +resharper_cpp_function_is_not_implemented_highlighting = warning +resharper_cpp_function_result_should_be_used_highlighting = hint +resharper_cpp_header_has_been_already_included_highlighting = hint +resharper_cpp_hidden_function_highlighting = warning +resharper_cpp_hiding_function_highlighting = warning +resharper_cpp_identical_operands_in_binary_expression_highlighting = warning +resharper_cpp_if_can_be_replaced_by_constexpr_if_highlighting = suggestion +resharper_cpp_implicit_default_constructor_not_available_highlighting = warning +resharper_cpp_incompatible_pointer_conversion_highlighting = warning +resharper_cpp_incomplete_switch_statement_highlighting = warning +resharper_cpp_inconsistent_naming_highlighting = hint +resharper_cpp_incorrect_blank_lines_near_braces_highlighting = none +resharper_cpp_initialized_value_is_always_rewritten_highlighting = warning +resharper_cpp_integral_to_pointer_conversion_highlighting = warning +resharper_cpp_invalid_line_continuation_highlighting = warning +resharper_cpp_join_declaration_and_assignment_highlighting = suggestion +resharper_cpp_lambda_capture_never_used_highlighting = warning +resharper_cpp_local_variable_may_be_const_highlighting = suggestion +resharper_cpp_local_variable_might_not_be_initialized_highlighting = warning +resharper_cpp_local_variable_with_non_trivial_dtor_is_never_used_highlighting = none +resharper_cpp_long_float_highlighting = warning +resharper_cpp_member_function_may_be_const_highlighting = suggestion +resharper_cpp_member_function_may_be_static_highlighting = suggestion +resharper_cpp_member_initializers_order_highlighting = suggestion +resharper_cpp_mismatched_class_tags_highlighting = warning +resharper_cpp_missing_blank_lines_highlighting = none +resharper_cpp_missing_include_guard_highlighting = warning +resharper_cpp_missing_indent_highlighting = none +resharper_cpp_missing_keyword_throw_highlighting = warning +resharper_cpp_missing_linebreak_highlighting = none +resharper_cpp_missing_space_highlighting = warning +resharper_cpp_ms_ext_address_of_class_r_value_highlighting = warning +resharper_cpp_ms_ext_binding_r_value_to_lvalue_reference_highlighting = warning +resharper_cpp_ms_ext_copy_elision_in_copy_init_declarator_highlighting = warning +resharper_cpp_ms_ext_double_user_conversion_in_copy_init_highlighting = warning +resharper_cpp_ms_ext_not_initialized_static_const_local_var_highlighting = warning +resharper_cpp_ms_ext_reinterpret_cast_from_nullptr_highlighting = warning +resharper_cpp_multiple_spaces_highlighting = warning +resharper_cpp_must_be_public_virtual_to_implement_interface_highlighting = warning +resharper_cpp_mutable_specifier_on_reference_member_highlighting = warning +resharper_cpp_nodiscard_function_without_return_value_highlighting = warning +resharper_cpp_non_exception_safe_resource_acquisition_highlighting = hint +resharper_cpp_non_explicit_conversion_operator_highlighting = hint +resharper_cpp_non_explicit_converting_constructor_highlighting = hint +resharper_cpp_non_inline_function_definition_in_header_file_highlighting = warning +resharper_cpp_non_inline_variable_definition_in_header_file_highlighting = warning +resharper_cpp_not_all_paths_return_value_highlighting = warning +resharper_cpp_no_discard_expression_highlighting = warning +resharper_cpp_object_member_might_not_be_initialized_highlighting = warning +resharper_cpp_outdent_is_off_prev_level_highlighting = none +resharper_cpp_out_parameter_must_be_written_highlighting = warning +resharper_cpp_parameter_may_be_const_highlighting = hint +resharper_cpp_parameter_may_be_const_ptr_or_ref_highlighting = suggestion +resharper_cpp_parameter_names_mismatch_highlighting = hint +resharper_cpp_parameter_never_used_highlighting = hint +resharper_cpp_parameter_value_is_reassigned_highlighting = warning +resharper_cpp_pass_value_parameter_by_const_reference_highlighting = suggestion +resharper_cpp_pointer_conversion_drops_qualifiers_highlighting = warning +resharper_cpp_pointer_to_integral_conversion_highlighting = warning +resharper_cpp_polymorphic_class_with_non_virtual_public_destructor_highlighting = warning +resharper_cpp_possibly_erroneous_empty_statements_highlighting = warning +resharper_cpp_possibly_uninitialized_member_highlighting = warning +resharper_cpp_possibly_unintended_object_slicing_highlighting = warning +resharper_cpp_precompiled_header_is_not_included_highlighting = error +resharper_cpp_precompiled_header_not_found_highlighting = error +resharper_cpp_printf_bad_format_highlighting = warning +resharper_cpp_printf_extra_arg_highlighting = warning +resharper_cpp_printf_missed_arg_highlighting = error +resharper_cpp_printf_risky_format_highlighting = warning +resharper_cpp_private_special_member_function_is_not_implemented_highlighting = warning +resharper_cpp_range_based_for_incompatible_reference_highlighting = warning +resharper_cpp_redefinition_of_default_argument_in_override_function_highlighting = warning +resharper_cpp_redundant_access_specifier_highlighting = hint +resharper_cpp_redundant_base_class_access_specifier_highlighting = hint +resharper_cpp_redundant_base_class_initializer_highlighting = suggestion +resharper_cpp_redundant_blank_lines_highlighting = none +resharper_cpp_redundant_boolean_expression_argument_highlighting = warning +resharper_cpp_redundant_cast_expression_highlighting = hint +resharper_cpp_redundant_complexity_in_comparison_highlighting = suggestion +resharper_cpp_redundant_const_specifier_highlighting = hint +resharper_cpp_redundant_control_flow_jump_highlighting = hint +resharper_cpp_redundant_dereferencing_and_taking_address_highlighting = suggestion +resharper_cpp_redundant_elaborated_type_specifier_highlighting = hint +resharper_cpp_redundant_else_keyword_highlighting = hint +resharper_cpp_redundant_else_keyword_inside_compound_statement_highlighting = hint +resharper_cpp_redundant_empty_declaration_highlighting = hint +resharper_cpp_redundant_empty_statement_highlighting = hint +resharper_cpp_redundant_inline_specifier_highlighting = hint +resharper_cpp_redundant_lambda_parameter_list_highlighting = hint +resharper_cpp_redundant_linebreak_highlighting = none +resharper_cpp_redundant_member_initializer_highlighting = suggestion +resharper_cpp_redundant_namespace_definition_highlighting = suggestion +resharper_cpp_redundant_parentheses_highlighting = hint +resharper_cpp_redundant_qualifier_highlighting = hint +resharper_cpp_redundant_space_highlighting = warning +resharper_cpp_redundant_static_specifier_on_member_allocation_function_highlighting = hint +resharper_cpp_redundant_static_specifier_on_thread_local_local_variable_highlighting = hint +resharper_cpp_redundant_template_arguments_highlighting = hint +resharper_cpp_redundant_template_keyword_highlighting = warning +resharper_cpp_redundant_typename_keyword_highlighting = warning +resharper_cpp_redundant_void_argument_list_highlighting = suggestion +resharper_cpp_reinterpret_cast_from_void_ptr_highlighting = suggestion +resharper_cpp_remove_redundant_braces_highlighting = none +resharper_cpp_replace_memset_with_zero_initialization_highlighting = suggestion +resharper_cpp_replace_tie_with_structured_binding_highlighting = suggestion +resharper_cpp_return_no_value_in_non_void_function_highlighting = warning +resharper_cpp_smart_pointer_vs_make_function_highlighting = suggestion +resharper_cpp_some_object_members_might_not_be_initialized_highlighting = warning +resharper_cpp_special_function_without_noexcept_specification_highlighting = warning +resharper_cpp_static_assert_failure_highlighting = error +resharper_cpp_static_data_member_in_unnamed_struct_highlighting = warning +resharper_cpp_static_specifier_on_anonymous_namespace_member_highlighting = suggestion +resharper_cpp_string_literal_to_char_pointer_conversion_highlighting = warning +resharper_cpp_tabs_and_spaces_mismatch_highlighting = none +resharper_cpp_tabs_are_disallowed_highlighting = none +resharper_cpp_tabs_outside_indent_highlighting = none +resharper_cpp_template_arguments_can_be_deduced_highlighting = hint +resharper_cpp_template_parameter_never_used_highlighting = hint +resharper_cpp_template_parameter_shadowing_highlighting = warning +resharper_cpp_this_arg_member_func_delegate_ctor_is_unsuported_by_dot_net_core_highlighting = none +resharper_cpp_throw_expression_can_be_replaced_with_rethrow_highlighting = warning +resharper_cpp_too_wide_scope_highlighting = suggestion +resharper_cpp_too_wide_scope_init_statement_highlighting = hint +resharper_cpp_type_alias_never_used_highlighting = warning +resharper_cpp_ue4_blueprint_callable_function_may_be_const_highlighting = hint +resharper_cpp_ue4_blueprint_callable_function_may_be_static_highlighting = hint +resharper_cpp_ue4_coding_standard_naming_violation_warning_highlighting = hint +resharper_cpp_ue4_coding_standard_u_class_naming_violation_error_highlighting = error +resharper_cpp_ue4_probable_memory_issues_with_u_objects_in_container_highlighting = warning +resharper_cpp_ue4_probable_memory_issues_with_u_object_highlighting = warning +resharper_cpp_ue_blueprint_callable_function_unused_highlighting = warning +resharper_cpp_ue_blueprint_implementable_event_not_implemented_highlighting = warning +resharper_cpp_ue_incorrect_engine_directory_highlighting = error +resharper_cpp_ue_non_existent_input_action_highlighting = warning +resharper_cpp_ue_non_existent_input_axis_highlighting = warning +resharper_cpp_ue_source_file_without_predefined_macros_highlighting = warning +resharper_cpp_ue_source_file_without_standard_library_highlighting = error +resharper_cpp_ue_version_file_doesnt_exist_highlighting = error +resharper_cpp_uninitialized_dependent_base_class_highlighting = warning +resharper_cpp_uninitialized_non_static_data_member_highlighting = warning +resharper_cpp_union_member_of_reference_type_highlighting = warning +resharper_cpp_unnamed_namespace_in_header_file_highlighting = warning +resharper_cpp_unnecessary_whitespace_highlighting = warning +resharper_cpp_unreachable_code_highlighting = warning +resharper_cpp_unsigned_zero_comparison_highlighting = warning +resharper_cpp_unused_include_directive_highlighting = warning +resharper_cpp_user_defined_literal_suffix_does_not_start_with_underscore_highlighting = warning +resharper_cpp_use_algorithm_with_count_highlighting = suggestion +resharper_cpp_use_associative_contains_highlighting = suggestion +resharper_cpp_use_auto_for_numeric_highlighting = hint +resharper_cpp_use_auto_highlighting = hint +resharper_cpp_use_elements_view_highlighting = suggestion +resharper_cpp_use_erase_algorithm_highlighting = suggestion +resharper_cpp_use_familiar_template_syntax_for_generic_lambdas_highlighting = suggestion +resharper_cpp_use_of_undeclared_class_highlighting = hint +resharper_cpp_use_range_algorithm_highlighting = suggestion +resharper_cpp_use_std_size_highlighting = suggestion +resharper_cpp_use_structured_binding_highlighting = hint +resharper_cpp_use_type_trait_alias_highlighting = suggestion +resharper_cpp_using_result_of_assignment_as_condition_highlighting = warning +resharper_cpp_u_function_macro_call_has_no_effect_highlighting = warning +resharper_cpp_u_property_macro_call_has_no_effect_highlighting = warning +resharper_cpp_variable_can_be_made_constexpr_highlighting = suggestion +resharper_cpp_virtual_function_call_inside_ctor_highlighting = warning +resharper_cpp_virtual_function_in_final_class_highlighting = warning +resharper_cpp_volatile_parameter_in_declaration_highlighting = suggestion +resharper_cpp_warning_directive_highlighting = warning +resharper_cpp_wrong_includes_order_highlighting = hint +resharper_cpp_wrong_indent_size_highlighting = none +resharper_cpp_wrong_slashes_in_include_directive_highlighting = hint +resharper_cpp_zero_constant_can_be_replaced_with_nullptr_highlighting = suggestion +resharper_cpp_zero_valued_expression_used_as_null_pointer_highlighting = warning +resharper_create_specialized_overload_highlighting = hint +resharper_css_browser_compatibility_highlighting = warning +resharper_css_caniuse_feature_requires_prefix_highlighting = hint +resharper_css_caniuse_unsupported_feature_highlighting = hint +resharper_css_not_resolved_highlighting = error +resharper_css_obsolete_highlighting = hint +resharper_css_property_does_not_override_vendor_property_highlighting = warning +resharper_cyclic_reference_comment_highlighting = none +resharper_c_declaration_with_implicit_int_type_highlighting = warning +resharper_c_sharp_build_cs_invalid_module_name_highlighting = warning +resharper_c_sharp_missing_plugin_dependency_highlighting = warning +resharper_declaration_hides_highlighting = hint +resharper_declaration_is_empty_highlighting = warning +resharper_declaration_visibility_error_highlighting = error +resharper_default_value_attribute_for_optional_parameter_highlighting = warning +resharper_deleting_non_qualified_reference_highlighting = error +resharper_dl_tag_contains_non_dt_or_dd_elements_highlighting = hint +resharper_double_colons_expected_highlighting = error +resharper_double_colons_preferred_highlighting = suggestion +resharper_double_negation_in_pattern_highlighting = suggestion +resharper_double_negation_of_boolean_highlighting = warning +resharper_double_negation_operator_highlighting = suggestion +resharper_duplicate_identifier_error_highlighting = error +resharper_duplicate_reference_comment_highlighting = warning +resharper_duplicate_resource_highlighting = warning +resharper_duplicating_local_declaration_highlighting = warning +resharper_duplicating_parameter_declaration_error_highlighting = error +resharper_duplicating_property_declaration_error_highlighting = error +resharper_duplicating_property_declaration_highlighting = warning +resharper_duplicating_switch_label_highlighting = warning +resharper_elided_trailing_element_highlighting = warning +resharper_empty_constructor_highlighting = warning +resharper_empty_destructor_highlighting = warning +resharper_empty_embedded_statement_highlighting = warning +resharper_empty_for_statement_highlighting = warning +resharper_empty_general_catch_clause_highlighting = warning +resharper_empty_namespace_highlighting = warning +resharper_empty_object_property_declaration_highlighting = error +resharper_empty_region_highlighting = suggestion +resharper_empty_return_value_for_type_annotated_function_highlighting = warning +resharper_empty_statement_highlighting = warning +resharper_empty_title_tag_highlighting = hint +resharper_enforce_do_while_statement_braces_highlighting = none +resharper_enforce_fixed_statement_braces_highlighting = none +resharper_enforce_foreach_statement_braces_highlighting = none +resharper_enforce_for_statement_braces_highlighting = none +resharper_enforce_if_statement_braces_highlighting = warning +resharper_enforce_lock_statement_braces_highlighting = none +resharper_enforce_using_statement_braces_highlighting = none +resharper_enforce_while_statement_braces_highlighting = none +resharper_entity_framework_n_plus_one_incomplete_data_query_highlighting = suggestion +resharper_entity_framework_n_plus_one_incomplete_data_usage_highlighting = warning +resharper_entity_framework_n_plus_one_query_highlighting = suggestion +resharper_entity_framework_n_plus_one_usage_highlighting = warning +resharper_entity_name_captured_only_global_highlighting = warning +resharper_entity_name_captured_only_local_highlighting = warning +resharper_enumerable_sum_in_explicit_unchecked_context_highlighting = warning +resharper_enum_underlying_type_is_int_highlighting = warning +resharper_equal_expression_comparison_highlighting = warning +resharper_error_in_xml_doc_reference_highlighting = error +resharper_es6_feature_highlighting = error +resharper_es7_feature_highlighting = error +resharper_eval_arguments_name_error_highlighting = error +resharper_event_exception_not_documented_highlighting = suggestion +resharper_event_never_invoked_global_highlighting = suggestion +resharper_event_never_subscribed_to_global_highlighting = suggestion +resharper_event_never_subscribed_to_local_highlighting = suggestion +resharper_event_unsubscription_via_anonymous_delegate_highlighting = warning +resharper_exception_not_documented_highlighting = hint +resharper_exception_not_documented_optional_highlighting = none +resharper_exception_not_thrown_highlighting = hint +resharper_exception_not_thrown_optional_highlighting = hint +resharper_exception_passed_as_template_argument_problem_highlighting = warning +resharper_experimental_feature_highlighting = error +resharper_explicit_caller_info_argument_highlighting = warning +resharper_expression_is_always_const_highlighting = warning +resharper_expression_is_always_null_highlighting = warning +resharper_extract_common_property_pattern_highlighting = hint +resharper_field_can_be_made_read_only_global_highlighting = suggestion +resharper_field_can_be_made_read_only_local_highlighting = suggestion +resharper_field_hides_interface_property_with_default_implementation_highlighting = warning +resharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting = hint +resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = hint +resharper_format_string_placeholders_mismatch_highlighting = warning +resharper_format_string_problem_highlighting = warning +resharper_for_can_be_converted_to_foreach_highlighting = suggestion +resharper_for_statement_condition_is_true_highlighting = warning +resharper_functions_used_before_declared_highlighting = none +resharper_function_complexity_overflow_highlighting = none +resharper_function_never_returns_highlighting = warning +resharper_function_parameter_named_arguments_highlighting = warning +resharper_function_recursive_on_all_paths_highlighting = warning +resharper_function_used_out_of_scope_highlighting = warning +resharper_gc_suppress_finalize_for_type_without_destructor_highlighting = warning +resharper_generic_enumerator_not_disposed_highlighting = warning +resharper_heap_view_boxing_allocation_highlighting = hint +resharper_heap_view_can_avoid_closure_highlighting = suggestion +resharper_heap_view_closure_allocation_highlighting = hint +resharper_heap_view_delegate_allocation_highlighting = hint +resharper_heap_view_implicit_capture_highlighting = none +resharper_heap_view_object_allocation_evident_highlighting = hint +resharper_heap_view_object_allocation_highlighting = hint +resharper_heap_view_object_allocation_possible_highlighting = hint +resharper_heap_view_possible_boxing_allocation_highlighting = hint +resharper_heuristically_unreachable_code_highlighting = warning +resharper_heuristic_unreachable_code_highlighting = warning +resharper_hex_color_value_with_alpha_highlighting = error +resharper_html_attributes_quotes_highlighting = hint +resharper_html_attribute_not_resolved_highlighting = warning +resharper_html_attribute_value_not_resolved_highlighting = warning +resharper_html_dead_code_highlighting = warning +resharper_html_event_not_resolved_highlighting = warning +resharper_html_id_duplication_highlighting = warning +resharper_html_id_not_resolved_highlighting = warning +resharper_html_obsolete_highlighting = warning +resharper_html_path_error_highlighting = warning +resharper_html_tag_not_closed_highlighting = error +resharper_html_tag_not_resolved_highlighting = warning +resharper_html_tag_should_be_self_closed_highlighting = warning +resharper_html_tag_should_not_be_self_closed_highlighting = warning +resharper_html_warning_highlighting = warning +resharper_identifier_typo_highlighting = none +resharper_if_std_is_constant_evaluated_can_be_replaced_highlighting = suggestion +resharper_implicit_any_error_highlighting = error +resharper_implicit_any_type_warning_highlighting = warning +resharper_import_keyword_not_with_invocation_highlighting = error +resharper_inactive_preprocessor_branch_highlighting = warning +resharper_inconsistently_synchronized_field_highlighting = warning +resharper_inconsistent_context_log_property_naming_highlighting = warning +resharper_inconsistent_function_returns_highlighting = warning +resharper_inconsistent_log_property_naming_highlighting = none +resharper_inconsistent_naming_highlighting = warning +resharper_inconsistent_order_of_locks_highlighting = warning +resharper_incorrect_blank_lines_near_braces_highlighting = warning +resharper_incorrect_operand_in_type_of_comparison_highlighting = warning +resharper_incorrect_triple_slash_location_highlighting = warning +resharper_indexing_by_invalid_range_highlighting = warning +resharper_inheritdoc_consider_usage_highlighting = none +resharper_inheritdoc_invalid_usage_highlighting = warning +resharper_inline_out_variable_declaration_highlighting = suggestion +resharper_inline_temporary_variable_highlighting = hint +resharper_internal_module_highlighting = suggestion +resharper_internal_or_private_member_not_documented_highlighting = warning +resharper_interpolated_string_expression_is_not_i_formattable_highlighting = warning +resharper_introduce_optional_parameters_global_highlighting = suggestion +resharper_introduce_optional_parameters_local_highlighting = suggestion +resharper_introduce_variable_to_apply_guard_highlighting = hint +resharper_int_division_by_zero_highlighting = warning +resharper_int_variable_overflow_highlighting = warning +resharper_int_variable_overflow_in_checked_context_highlighting = warning +resharper_int_variable_overflow_in_unchecked_context_highlighting = warning +resharper_invalid_attribute_value_highlighting = warning +resharper_invalid_json_syntax_highlighting = error +resharper_invalid_task_element_highlighting = none +resharper_invalid_value_highlighting = error +resharper_invalid_value_type_highlighting = warning +resharper_invalid_xml_doc_comment_highlighting = warning +resharper_invert_condition_1_highlighting = hint +resharper_invert_if_highlighting = hint +resharper_invocation_is_skipped_highlighting = hint +resharper_invocation_of_non_function_highlighting = warning +resharper_invoked_expression_maybe_non_function_highlighting = warning +resharper_invoke_as_extension_method_highlighting = suggestion +resharper_is_expression_always_false_highlighting = warning +resharper_is_expression_always_true_highlighting = warning +resharper_iterator_method_result_is_ignored_highlighting = warning +resharper_iterator_never_returns_highlighting = warning +resharper_join_declaration_and_initializer_highlighting = suggestion +resharper_join_declaration_and_initializer_js_highlighting = suggestion +resharper_join_null_check_with_usage_highlighting = suggestion +resharper_json_validation_failed_highlighting = error +resharper_js_path_not_found_highlighting = error +resharper_js_unreachable_code_highlighting = warning +resharper_jump_must_be_in_loop_highlighting = warning +resharper_label_or_semicolon_expected_highlighting = error +resharper_lambda_expression_can_be_made_static_highlighting = none +resharper_lambda_expression_must_be_static_highlighting = suggestion +resharper_lambda_highlighting = suggestion +resharper_lambda_should_not_capture_context_highlighting = warning +resharper_less_specific_overload_than_main_signature_highlighting = warning +resharper_lexical_declaration_needs_block_highlighting = error +resharper_localizable_element_highlighting = warning +resharper_local_function_can_be_made_static_highlighting = none +resharper_local_function_hides_method_highlighting = warning +resharper_local_function_redefined_later_highlighting = warning +resharper_local_variable_hides_member_highlighting = warning +resharper_log_message_is_sentence_problem_highlighting = warning +resharper_long_literal_ending_lower_l_highlighting = warning +resharper_loop_can_be_converted_to_query_highlighting = hint +resharper_loop_can_be_partly_converted_to_query_highlighting = none +resharper_loop_variable_is_never_changed_inside_loop_highlighting = warning +resharper_l_value_is_expected_highlighting = error +resharper_markup_attribute_typo_highlighting = suggestion +resharper_markup_text_typo_highlighting = suggestion +resharper_math_abs_method_is_redundant_highlighting = warning +resharper_math_clamp_min_greater_than_max_highlighting = warning +resharper_meaningless_default_parameter_value_highlighting = warning +resharper_member_can_be_file_local_highlighting = none +resharper_member_can_be_internal_highlighting = none +resharper_member_can_be_made_static_global_highlighting = hint +resharper_member_can_be_made_static_local_highlighting = hint +resharper_member_can_be_private_global_highlighting = none +resharper_member_can_be_private_local_highlighting = suggestion +resharper_member_can_be_protected_global_highlighting = none +resharper_member_can_be_protected_local_highlighting = suggestion +resharper_member_hides_interface_member_with_default_implementation_highlighting = warning +resharper_member_hides_static_from_outer_class_highlighting = warning +resharper_member_initializer_value_ignored_highlighting = warning +resharper_merge_and_pattern_highlighting = suggestion +resharper_merge_cast_with_type_check_highlighting = suggestion +resharper_merge_conditional_expression_highlighting = suggestion +resharper_merge_into_logical_pattern_highlighting = hint +resharper_merge_into_negated_pattern_highlighting = hint +resharper_merge_into_pattern_highlighting = suggestion +resharper_merge_nested_property_patterns_highlighting = suggestion +resharper_merge_sequential_checks_highlighting = hint +resharper_method_has_async_overload_highlighting = suggestion +resharper_method_has_async_overload_with_cancellation_highlighting = suggestion +resharper_method_overload_with_optional_parameter_highlighting = warning +resharper_method_safe_this_highlighting = suggestion +resharper_method_supports_cancellation_highlighting = suggestion +resharper_missing_alt_attribute_in_img_tag_highlighting = hint +resharper_missing_attribute_highlighting = warning +resharper_missing_blank_lines_highlighting = warning +resharper_missing_body_tag_highlighting = warning +resharper_missing_has_own_property_in_foreach_highlighting = warning +resharper_missing_head_and_body_tags_highlighting = warning +resharper_missing_head_tag_highlighting = warning +resharper_missing_indent_highlighting = warning +resharper_missing_linebreak_highlighting = warning +resharper_missing_space_highlighting = warning +resharper_missing_title_tag_highlighting = hint +resharper_missing_xml_doc_highlighting = none +resharper_misuse_of_owner_function_this_highlighting = warning +resharper_more_specific_foreach_variable_type_available_highlighting = suggestion +resharper_more_specific_signature_after_less_specific_highlighting = warning +resharper_move_local_function_after_jump_statement_highlighting = hint +resharper_move_to_existing_positional_deconstruction_pattern_highlighting = hint +resharper_move_variable_declaration_inside_loop_condition_highlighting = suggestion +resharper_multiple_declarations_in_foreach_highlighting = error +resharper_multiple_nullable_attributes_usage_highlighting = warning +resharper_multiple_order_by_highlighting = warning +resharper_multiple_output_tags_highlighting = warning +resharper_multiple_resolve_candidates_in_text_highlighting = warning +resharper_multiple_spaces_highlighting = warning +resharper_multiple_statements_on_one_line_highlighting = warning +resharper_multiple_type_members_on_one_line_highlighting = warning +resharper_must_use_return_value_highlighting = warning +resharper_mvc_action_not_resolved_highlighting = error +resharper_mvc_area_not_resolved_highlighting = error +resharper_mvc_controller_not_resolved_highlighting = error +resharper_mvc_invalid_model_type_highlighting = error +resharper_mvc_masterpage_not_resolved_highlighting = error +resharper_mvc_partial_view_not_resolved_highlighting = error +resharper_mvc_template_not_resolved_highlighting = error +resharper_mvc_view_component_not_resolved_highlighting = error +resharper_mvc_view_component_view_not_resolved_highlighting = error +resharper_mvc_view_not_resolved_highlighting = error +resharper_native_type_prototype_extending_highlighting = warning +resharper_native_type_prototype_overwriting_highlighting = warning +resharper_negation_of_relational_pattern_highlighting = suggestion +resharper_negative_equality_expression_highlighting = suggestion +resharper_negative_index_highlighting = warning +resharper_nested_string_interpolation_highlighting = suggestion +resharper_non_assigned_constant_highlighting = error +resharper_non_atomic_compound_operator_highlighting = warning +resharper_non_constant_equality_expression_has_constant_result_highlighting = warning +resharper_non_parsable_element_highlighting = warning +resharper_non_readonly_member_in_get_hash_code_highlighting = none +resharper_non_volatile_field_in_double_check_locking_highlighting = warning +resharper_not_accessed_field_global_highlighting = suggestion +resharper_not_accessed_field_local_highlighting = warning +resharper_not_accessed_out_parameter_variable_highlighting = warning +resharper_not_accessed_positional_property_global_highlighting = warning +resharper_not_accessed_positional_property_local_highlighting = warning +resharper_not_accessed_variable_highlighting = warning +resharper_not_all_paths_return_value_highlighting = warning +resharper_not_assigned_out_parameter_highlighting = warning +resharper_not_declared_in_parent_culture_highlighting = warning +resharper_not_null_or_required_member_is_not_initialized_highlighting = warning +resharper_not_observable_annotation_redundancy_highlighting = warning +resharper_not_overridden_in_specific_culture_highlighting = warning +resharper_not_resolved_highlighting = warning +resharper_not_resolved_in_text_highlighting = warning +resharper_nullable_warning_suppression_is_used_highlighting = none +resharper_nullness_annotation_conflict_with_jet_brains_annotations_highlighting = warning +resharper_null_coalescing_condition_is_always_not_null_according_to_api_contract_highlighting = warning +resharper_n_unit_async_method_must_be_task_highlighting = warning +resharper_n_unit_attribute_produces_too_many_tests_highlighting = none +resharper_n_unit_auto_fixture_incorrect_argument_type_highlighting = warning +resharper_n_unit_auto_fixture_missed_test_attribute_highlighting = warning +resharper_n_unit_auto_fixture_missed_test_or_test_fixture_attribute_highlighting = warning +resharper_n_unit_auto_fixture_redundant_argument_in_inline_auto_data_attribute_highlighting = warning +resharper_n_unit_duplicate_values_highlighting = warning +resharper_n_unit_ignored_parameter_attribute_highlighting = warning +resharper_n_unit_implicit_unspecified_null_values_highlighting = warning +resharper_n_unit_incorrect_argument_type_highlighting = warning +resharper_n_unit_incorrect_expected_result_type_highlighting = warning +resharper_n_unit_incorrect_range_bounds_highlighting = warning +resharper_n_unit_method_with_parameters_and_test_attribute_highlighting = warning +resharper_n_unit_missing_arguments_in_test_case_attribute_highlighting = warning +resharper_n_unit_non_public_method_with_test_attribute_highlighting = warning +resharper_n_unit_no_values_provided_highlighting = warning +resharper_n_unit_parameter_type_is_not_compatible_with_attribute_highlighting = warning +resharper_n_unit_range_attribute_bounds_are_out_of_range_highlighting = warning +resharper_n_unit_range_step_sign_mismatch_highlighting = warning +resharper_n_unit_range_step_value_must_not_be_zero_highlighting = warning +resharper_n_unit_range_to_value_is_not_reachable_highlighting = warning +resharper_n_unit_redundant_argument_instead_of_expected_result_highlighting = warning +resharper_n_unit_redundant_argument_in_test_case_attribute_highlighting = warning +resharper_n_unit_redundant_expected_result_in_test_case_attribute_highlighting = warning +resharper_n_unit_test_case_attribute_requires_expected_result_highlighting = warning +resharper_n_unit_test_case_result_property_duplicates_expected_result_highlighting = warning +resharper_n_unit_test_case_result_property_is_obsolete_highlighting = warning +resharper_n_unit_test_case_source_cannot_be_resolved_highlighting = warning +resharper_n_unit_test_case_source_must_be_field_property_method_highlighting = warning +resharper_n_unit_test_case_source_must_be_static_highlighting = warning +resharper_n_unit_test_case_source_should_implement_i_enumerable_highlighting = warning +resharper_object_creation_as_statement_highlighting = warning +resharper_object_destructuring_without_parentheses_highlighting = error +resharper_object_literals_are_not_comma_free_highlighting = error +resharper_obsolete_element_error_highlighting = error +resharper_obsolete_element_highlighting = warning +resharper_octal_literals_not_allowed_error_highlighting = error +resharper_ol_tag_contains_non_li_elements_highlighting = hint +resharper_one_way_operation_contract_with_return_type_highlighting = warning +resharper_operation_contract_without_service_contract_highlighting = warning +resharper_operator_is_can_be_used_highlighting = warning +resharper_operator_without_matched_checked_operator_highlighting = warning +resharper_optional_parameter_hierarchy_mismatch_highlighting = warning +resharper_optional_parameter_ref_out_highlighting = warning +resharper_other_tags_inside_script1_highlighting = error +resharper_other_tags_inside_script2_highlighting = error +resharper_other_tags_inside_unclosed_script_highlighting = error +resharper_outdent_is_off_prev_level_highlighting = warning +resharper_output_tag_required_highlighting = warning +resharper_out_parameter_value_is_always_discarded_global_highlighting = suggestion +resharper_out_parameter_value_is_always_discarded_local_highlighting = warning +resharper_overload_signature_inferring_highlighting = hint +resharper_overridden_with_empty_value_highlighting = warning +resharper_overridden_with_same_value_highlighting = suggestion +resharper_parameter_doesnt_make_any_sense_highlighting = warning +resharper_parameter_hides_member_highlighting = warning +resharper_parameter_only_used_for_precondition_check_global_highlighting = suggestion +resharper_parameter_only_used_for_precondition_check_local_highlighting = warning +resharper_parameter_type_can_be_enumerable_global_highlighting = hint +resharper_parameter_type_can_be_enumerable_local_highlighting = hint +resharper_parameter_value_is_not_used_highlighting = warning +resharper_partial_method_parameter_name_mismatch_highlighting = warning +resharper_partial_method_with_single_part_highlighting = warning +resharper_partial_type_with_single_part_highlighting = warning +resharper_pass_string_interpolation_highlighting = hint +resharper_path_not_resolved_highlighting = error +resharper_pattern_always_matches_highlighting = warning +resharper_pattern_is_always_true_or_false_highlighting = warning +resharper_pattern_is_redundant_highlighting = warning +resharper_pattern_never_matches_highlighting = warning +resharper_place_assignment_expression_into_block_highlighting = none +resharper_polymorphic_field_like_event_invocation_highlighting = warning +resharper_positional_property_used_problem_highlighting = warning +resharper_possible_infinite_inheritance_highlighting = warning +resharper_possible_intended_rethrow_highlighting = warning +resharper_possible_interface_member_ambiguity_highlighting = warning +resharper_possible_invalid_cast_exception_highlighting = warning +resharper_possible_invalid_cast_exception_in_foreach_loop_highlighting = warning +resharper_possible_invalid_operation_exception_highlighting = warning +resharper_possible_loss_of_fraction_highlighting = warning +resharper_possible_mistaken_argument_highlighting = warning +resharper_possible_mistaken_call_to_get_type_1_highlighting = warning +resharper_possible_mistaken_call_to_get_type_2_highlighting = warning +resharper_possible_multiple_enumeration_highlighting = warning +resharper_possible_multiple_write_access_in_double_check_locking_highlighting = warning +resharper_possible_null_reference_exception_highlighting = warning +resharper_possible_struct_member_modification_of_non_variable_struct_highlighting = warning +resharper_possible_unintended_linear_search_in_set_highlighting = warning +resharper_possible_unintended_queryable_as_enumerable_highlighting = suggestion +resharper_possible_unintended_reference_comparison_highlighting = warning +resharper_possible_write_to_me_highlighting = warning +resharper_possibly_impure_method_call_on_readonly_variable_highlighting = warning +resharper_possibly_incorrectly_broken_statement_highlighting = warning +resharper_possibly_missing_indexer_initializer_comma_highlighting = warning +resharper_possibly_mistaken_use_of_interpolated_string_insert_highlighting = warning +resharper_possibly_unassigned_property_highlighting = hint +resharper_possibly_unintended_usage_parameterless_get_expression_type_highlighting = error +resharper_private_field_can_be_converted_to_local_variable_highlighting = warning +resharper_private_variable_can_be_made_readonly_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = suggestion +resharper_property_can_be_made_init_only_local_highlighting = suggestion +resharper_property_field_keyword_is_never_assigned_highlighting = warning +resharper_property_field_keyword_is_never_used_highlighting = warning +resharper_property_getter_cannot_have_parameters_highlighting = error +resharper_property_not_resolved_highlighting = error +resharper_property_setter_must_have_single_parameter_highlighting = error +resharper_public_constructor_in_abstract_class_highlighting = suggestion +resharper_pure_attribute_on_void_method_highlighting = warning +resharper_qualified_expression_is_null_highlighting = warning +resharper_qualified_expression_maybe_null_highlighting = warning +resharper_raw_string_can_be_simplified_highlighting = hint +resharper_razor_layout_not_resolved_highlighting = error +resharper_razor_section_not_resolved_highlighting = error +resharper_read_access_in_double_check_locking_highlighting = warning +resharper_redundant_abstract_modifier_highlighting = warning +resharper_redundant_accessor_body_highlighting = suggestion +resharper_redundant_always_match_subpattern_highlighting = suggestion +resharper_redundant_anonymous_type_property_name_highlighting = warning +resharper_redundant_argument_default_value_highlighting = warning +resharper_redundant_array_creation_expression_highlighting = hint +resharper_redundant_array_lower_bound_specification_highlighting = warning +resharper_redundant_assignment_highlighting = warning +resharper_redundant_attribute_parentheses_highlighting = hint +resharper_redundant_attribute_suffix_highlighting = warning +resharper_redundant_attribute_usage_property_highlighting = suggestion +resharper_redundant_base_constructor_call_highlighting = warning +resharper_redundant_base_qualifier_highlighting = warning +resharper_redundant_blank_lines_highlighting = warning +resharper_redundant_block_highlighting = warning +resharper_redundant_bool_compare_highlighting = warning +resharper_redundant_caller_argument_expression_default_value_highlighting = warning +resharper_redundant_case_label_highlighting = warning +resharper_redundant_cast_highlighting = warning +resharper_redundant_catch_clause_highlighting = warning +resharper_redundant_check_before_assignment_highlighting = warning +resharper_redundant_collection_initializer_element_braces_highlighting = hint +resharper_redundant_comparison_with_boolean_highlighting = warning +resharper_redundant_configure_await_highlighting = suggestion +resharper_redundant_css_hack_highlighting = warning +resharper_redundant_declaration_semicolon_highlighting = hint +resharper_redundant_default_member_initializer_highlighting = none +resharper_redundant_delegate_creation_highlighting = warning +resharper_redundant_dictionary_contains_key_before_adding_highlighting = warning +resharper_redundant_disable_warning_comment_highlighting = warning +resharper_redundant_discard_designation_highlighting = suggestion +resharper_redundant_else_block_highlighting = warning +resharper_redundant_empty_case_else_highlighting = warning +resharper_redundant_empty_constructor_highlighting = warning +resharper_redundant_empty_finally_block_highlighting = warning +resharper_redundant_empty_object_creation_argument_list_highlighting = hint +resharper_redundant_empty_object_or_collection_initializer_highlighting = warning +resharper_redundant_empty_switch_section_highlighting = warning +resharper_redundant_enumerable_cast_call_highlighting = warning +resharper_redundant_enum_case_label_for_default_section_highlighting = none +resharper_redundant_explicit_array_creation_highlighting = warning +resharper_redundant_explicit_array_size_highlighting = warning +resharper_redundant_explicit_nullable_creation_highlighting = warning +resharper_redundant_explicit_params_array_creation_highlighting = suggestion +resharper_redundant_explicit_positional_property_declaration_highlighting = warning +resharper_redundant_explicit_tuple_component_name_highlighting = warning +resharper_redundant_extends_list_entry_highlighting = warning +resharper_redundant_fixed_pointer_declaration_highlighting = suggestion +resharper_redundant_highlighting = warning +resharper_redundant_if_else_block_highlighting = hint +resharper_redundant_if_statement_then_keyword_highlighting = none +resharper_redundant_immediate_delegate_invocation_highlighting = suggestion +resharper_redundant_intermediate_variable_highlighting = hint +resharper_redundant_is_before_relational_pattern_highlighting = suggestion +resharper_redundant_iterator_keyword_highlighting = warning +resharper_redundant_jump_statement_highlighting = warning +resharper_redundant_lambda_parameter_type_highlighting = warning +resharper_redundant_lambda_signature_parentheses_highlighting = hint +resharper_redundant_linebreak_highlighting = none +resharper_redundant_local_class_name_highlighting = hint +resharper_redundant_local_function_name_highlighting = hint +resharper_redundant_logical_conditional_expression_operand_highlighting = warning +resharper_redundant_me_qualifier_highlighting = warning +resharper_redundant_my_base_qualifier_highlighting = warning +resharper_redundant_my_class_qualifier_highlighting = warning +resharper_redundant_name_qualifier_highlighting = warning +resharper_redundant_not_null_constraint_highlighting = warning +resharper_redundant_nullable_annotation_on_reference_type_constraint_highlighting = warning +resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_base_type_highlighting = warning +resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_type_kind_highlighting = warning +resharper_redundant_nullable_directive_highlighting = warning +resharper_redundant_nullable_flow_attribute_highlighting = warning +resharper_redundant_nullable_type_mark_highlighting = warning +resharper_redundant_nullness_attribute_with_nullable_reference_types_highlighting = warning +resharper_redundant_overflow_checking_context_highlighting = warning +resharper_redundant_overload_global_highlighting = suggestion +resharper_redundant_overload_local_highlighting = suggestion +resharper_redundant_overridden_member_highlighting = warning +resharper_redundant_params_highlighting = warning +resharper_redundant_parentheses_highlighting = none +resharper_redundant_parent_type_declaration_highlighting = warning +resharper_redundant_pattern_parentheses_highlighting = hint +resharper_redundant_property_parentheses_highlighting = hint +resharper_redundant_property_pattern_clause_highlighting = suggestion +resharper_redundant_qualifier_highlighting = warning +resharper_redundant_query_order_by_ascending_keyword_highlighting = hint +resharper_redundant_range_bound_highlighting = suggestion +resharper_redundant_readonly_modifier_highlighting = suggestion +resharper_redundant_record_class_keyword_highlighting = warning +resharper_redundant_scoped_parameter_modifier_highlighting = warning +resharper_redundant_setter_value_parameter_declaration_highlighting = hint +resharper_redundant_set_contains_before_adding_highlighting = warning +resharper_redundant_space_highlighting = warning +resharper_redundant_string_format_call_highlighting = warning +resharper_redundant_string_interpolation_highlighting = suggestion +resharper_redundant_string_to_char_array_call_highlighting = warning +resharper_redundant_string_type_highlighting = suggestion +resharper_redundant_suppress_nullable_warning_expression_highlighting = warning +resharper_redundant_ternary_expression_highlighting = warning +resharper_redundant_to_string_call_for_value_type_highlighting = hint +resharper_redundant_to_string_call_highlighting = warning +resharper_redundant_type_arguments_of_method_highlighting = warning +resharper_redundant_type_cast_highlighting = warning +resharper_redundant_type_cast_structural_highlighting = warning +resharper_redundant_type_check_in_pattern_highlighting = warning +resharper_redundant_type_declaration_body_highlighting = warning +resharper_redundant_units_highlighting = warning +resharper_redundant_unsafe_context_highlighting = warning +resharper_redundant_using_directive_global_highlighting = warning +resharper_redundant_using_directive_highlighting = warning +resharper_redundant_variable_type_specification_highlighting = hint +resharper_redundant_verbatim_prefix_highlighting = suggestion +resharper_redundant_verbatim_string_prefix_highlighting = suggestion +resharper_redundant_virtual_modifier_highlighting = warning +resharper_redundant_with_cancellation_highlighting = warning +resharper_redundant_with_expression_highlighting = suggestion +resharper_reference_equals_with_value_type_highlighting = warning +resharper_reg_exp_inspections_highlighting = warning +resharper_remove_constructor_invocation_highlighting = none +resharper_remove_redundant_braces_highlighting = suggestion +resharper_remove_redundant_or_statement_false_highlighting = suggestion +resharper_remove_redundant_or_statement_true_highlighting = suggestion +resharper_remove_to_list_1_highlighting = suggestion +resharper_remove_to_list_2_highlighting = suggestion +resharper_replace_auto_property_with_computed_property_highlighting = hint +resharper_replace_conditional_expression_with_null_coalescing_highlighting = suggestion +resharper_replace_indicing_with_array_destructuring_highlighting = hint +resharper_replace_indicing_with_short_hand_properties_after_destructuring_highlighting = hint +resharper_replace_object_pattern_with_var_pattern_highlighting = suggestion +resharper_replace_sequence_equal_with_constant_pattern_highlighting = suggestion +resharper_replace_slice_with_range_indexer_highlighting = hint +resharper_replace_substring_with_range_indexer_highlighting = hint +resharper_replace_undefined_checking_series_with_object_destructuring_highlighting = hint +resharper_replace_with_destructuring_swap_highlighting = hint +resharper_replace_with_field_keyword_highlighting = suggestion +resharper_replace_with_first_or_default_1_highlighting = suggestion +resharper_replace_with_first_or_default_2_highlighting = suggestion +resharper_replace_with_first_or_default_3_highlighting = suggestion +resharper_replace_with_first_or_default_4_highlighting = suggestion +resharper_replace_with_last_or_default_1_highlighting = suggestion +resharper_replace_with_last_or_default_2_highlighting = suggestion +resharper_replace_with_last_or_default_3_highlighting = suggestion +resharper_replace_with_last_or_default_4_highlighting = suggestion +resharper_replace_with_of_type_1_highlighting = suggestion +resharper_replace_with_of_type_2_highlighting = suggestion +resharper_replace_with_of_type_3_highlighting = suggestion +resharper_replace_with_of_type_any_1_highlighting = suggestion +resharper_replace_with_of_type_any_2_highlighting = suggestion +resharper_replace_with_of_type_count_1_highlighting = suggestion +resharper_replace_with_of_type_count_2_highlighting = suggestion +resharper_replace_with_of_type_first_1_highlighting = suggestion +resharper_replace_with_of_type_first_2_highlighting = suggestion +resharper_replace_with_of_type_first_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_first_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_last_1_highlighting = suggestion +resharper_replace_with_of_type_last_2_highlighting = suggestion +resharper_replace_with_of_type_last_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_last_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_long_count_highlighting = suggestion +resharper_replace_with_of_type_single_1_highlighting = suggestion +resharper_replace_with_of_type_single_2_highlighting = suggestion +resharper_replace_with_of_type_single_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_single_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_where_highlighting = suggestion +resharper_replace_with_primary_constructor_parameter_highlighting = suggestion +resharper_replace_with_simple_assignment_false_highlighting = suggestion +resharper_replace_with_simple_assignment_true_highlighting = suggestion +resharper_replace_with_single_assignment_false_highlighting = suggestion +resharper_replace_with_single_assignment_true_highlighting = suggestion +resharper_replace_with_single_call_to_any_highlighting = suggestion +resharper_replace_with_single_call_to_count_highlighting = suggestion +resharper_replace_with_single_call_to_first_highlighting = suggestion +resharper_replace_with_single_call_to_first_or_default_highlighting = suggestion +resharper_replace_with_single_call_to_last_highlighting = suggestion +resharper_replace_with_single_call_to_last_or_default_highlighting = suggestion +resharper_replace_with_single_call_to_single_highlighting = suggestion +resharper_replace_with_single_call_to_single_or_default_highlighting = suggestion +resharper_replace_with_single_or_default_1_highlighting = suggestion +resharper_replace_with_single_or_default_2_highlighting = suggestion +resharper_replace_with_single_or_default_3_highlighting = suggestion +resharper_replace_with_single_or_default_4_highlighting = suggestion +resharper_replace_with_string_is_null_or_empty_highlighting = suggestion +resharper_required_base_types_conflict_highlighting = warning +resharper_required_base_types_direct_conflict_highlighting = warning +resharper_required_base_types_is_not_inherited_highlighting = warning +resharper_requires_fallback_color_highlighting = warning +resharper_resource_item_not_resolved_highlighting = error +resharper_resource_not_resolved_highlighting = error +resharper_resx_not_resolved_highlighting = warning +resharper_return_from_global_scopet_with_value_highlighting = warning +resharper_return_of_task_produced_by_using_variable_highlighting = warning +resharper_return_of_using_variable_highlighting = warning +resharper_return_type_can_be_enumerable_global_highlighting = hint +resharper_return_type_can_be_enumerable_local_highlighting = hint +resharper_return_type_can_be_not_nullable_highlighting = warning +resharper_return_value_of_pure_method_is_not_used_highlighting = warning +resharper_route_templates_action_route_prefix_can_be_extracted_to_controller_route_highlighting = hint +resharper_route_templates_ambiguous_matching_constraint_constructor_highlighting = warning +resharper_route_templates_constraint_argument_cannot_be_converted_highlighting = warning +resharper_route_templates_controller_route_parameter_is_not_passed_to_methods_highlighting = hint +resharper_route_templates_duplicated_parameter_highlighting = warning +resharper_route_templates_matching_constraint_constructor_not_resolved_highlighting = warning +resharper_route_templates_method_missing_route_parameters_highlighting = hint +resharper_route_templates_optional_parameter_can_be_preceded_only_by_single_period_highlighting = warning +resharper_route_templates_optional_parameter_must_be_at_the_end_of_segment_highlighting = warning +resharper_route_templates_parameter_constraint_can_be_specified_highlighting = hint +resharper_route_templates_parameter_type_and_constraints_mismatch_highlighting = warning +resharper_route_templates_parameter_type_can_be_made_stricter_highlighting = suggestion +resharper_route_templates_route_parameter_constraint_not_resolved_highlighting = warning +resharper_route_templates_route_parameter_is_not_passed_to_method_highlighting = hint +resharper_route_templates_route_token_not_resolved_highlighting = warning +resharper_route_templates_symbol_not_resolved_highlighting = warning +resharper_route_templates_syntax_error_highlighting = warning +resharper_safe_cast_is_used_as_type_check_highlighting = suggestion +resharper_same_imports_with_different_name_highlighting = warning +resharper_same_variable_assignment_highlighting = warning +resharper_script_tag_has_both_src_and_content_attributes_highlighting = error +resharper_script_tag_with_content_before_includes_highlighting = hint +resharper_sealed_member_in_sealed_class_highlighting = warning +resharper_separate_control_transfer_statement_highlighting = suggestion +resharper_separate_local_functions_with_jump_statement_highlighting = hint +resharper_service_contract_without_operations_highlighting = warning +resharper_shift_expression_real_shift_count_is_zero_highlighting = warning +resharper_shift_expression_result_equals_zero_highlighting = warning +resharper_shift_expression_right_operand_not_equal_real_count_highlighting = warning +resharper_shift_expression_zero_left_operand_highlighting = warning +resharper_similar_anonymous_type_nearby_highlighting = hint +resharper_similar_expressions_comparison_highlighting = warning +resharper_simplify_conditional_operator_highlighting = suggestion +resharper_simplify_conditional_ternary_expression_highlighting = suggestion +resharper_simplify_i_if_highlighting = suggestion +resharper_simplify_linq_expression_use_all_highlighting = suggestion +resharper_simplify_linq_expression_use_any_highlighting = suggestion +resharper_simplify_linq_expression_use_min_by_and_max_by_highlighting = suggestion +resharper_simplify_string_interpolation_highlighting = suggestion +resharper_specify_a_culture_in_string_conversion_explicitly_highlighting = warning +resharper_specify_string_comparison_highlighting = hint +resharper_specify_variable_type_explicitly_highlighting = hint +resharper_spin_lock_in_readonly_field_highlighting = warning +resharper_stack_alloc_inside_loop_highlighting = warning +resharper_statement_termination_highlighting = warning +resharper_static_member_initializer_referes_to_member_below_highlighting = warning +resharper_static_member_in_generic_type_highlighting = warning +resharper_static_problem_in_text_highlighting = warning +resharper_std_is_constant_evaluated_will_always_evaluate_to_constant_highlighting = warning +resharper_string_compare_is_culture_specific_1_highlighting = warning +resharper_string_compare_is_culture_specific_2_highlighting = warning +resharper_string_compare_is_culture_specific_3_highlighting = warning +resharper_string_compare_is_culture_specific_4_highlighting = warning +resharper_string_compare_is_culture_specific_5_highlighting = warning +resharper_string_compare_is_culture_specific_6_highlighting = warning +resharper_string_compare_to_is_culture_specific_highlighting = warning +resharper_string_concatenation_to_template_string_highlighting = hint +resharper_string_ends_with_is_culture_specific_highlighting = warning +resharper_string_index_of_is_culture_specific_1_highlighting = warning +resharper_string_index_of_is_culture_specific_2_highlighting = warning +resharper_string_index_of_is_culture_specific_3_highlighting = warning +resharper_string_last_index_of_is_culture_specific_1_highlighting = warning +resharper_string_last_index_of_is_culture_specific_2_highlighting = warning +resharper_string_last_index_of_is_culture_specific_3_highlighting = warning +resharper_string_literal_as_interpolation_argument_highlighting = suggestion +resharper_string_literal_typo_highlighting = none +resharper_string_literal_wrong_quotes_highlighting = hint +resharper_string_starts_with_is_culture_specific_highlighting = warning +resharper_structured_message_template_problem_highlighting = warning +resharper_struct_can_be_made_read_only_highlighting = suggestion +resharper_struct_member_can_be_made_read_only_highlighting = none +resharper_suggest_base_type_for_parameter_highlighting = hint +resharper_suggest_base_type_for_parameter_in_constructor_highlighting = hint +resharper_suggest_discard_declaration_var_style_highlighting = hint +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_deconstruction_declarations_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_super_call_highlighting = suggestion +resharper_super_call_prohibits_this_highlighting = error +resharper_suppress_nullable_warning_expression_as_inverted_is_expression_highlighting = warning +resharper_suspicious_instanceof_check_highlighting = warning +resharper_suspicious_lambda_block_highlighting = warning +resharper_suspicious_lock_over_synchronization_primitive_highlighting = warning +resharper_suspicious_math_sign_method_highlighting = warning +resharper_suspicious_parameter_name_in_argument_null_exception_highlighting = warning +resharper_suspicious_this_usage_highlighting = warning +resharper_suspicious_typeof_check_highlighting = warning +resharper_suspicious_type_conversion_global_highlighting = warning +resharper_swap_via_deconstruction_highlighting = suggestion +resharper_switch_expression_handles_some_known_enum_values_with_exception_in_default_highlighting = hint +resharper_switch_statement_for_enum_misses_default_section_highlighting = hint +resharper_switch_statement_handles_some_known_enum_values_with_default_highlighting = hint +resharper_switch_statement_missing_some_enum_cases_no_default_highlighting = hint +resharper_symbol_from_not_copied_locally_reference_used_warning_highlighting = warning +resharper_syntax_is_not_allowed_highlighting = warning +resharper_tabs_and_spaces_mismatch_highlighting = warning +resharper_tabs_are_disallowed_highlighting = none +resharper_tabs_outside_indent_highlighting = warning +resharper_tail_recursive_call_highlighting = hint +resharper_tasks_not_loaded_highlighting = warning +resharper_template_duplicate_property_problem_highlighting = warning +resharper_template_format_string_problem_highlighting = warning +resharper_template_is_not_compile_time_constant_problem_highlighting = warning +resharper_ternary_can_be_replaced_by_its_condition_highlighting = warning +resharper_this_in_global_context_highlighting = warning +resharper_thread_static_at_instance_field_highlighting = warning +resharper_thread_static_field_has_initializer_highlighting = warning +resharper_throwing_system_exception_highlighting = suggestion +resharper_throw_from_catch_with_no_inner_exception_highlighting = warning +resharper_throw_must_be_followed_by_expression_highlighting = error +resharper_too_wide_local_variable_scope_highlighting = suggestion +resharper_try_cast_always_succeeds_highlighting = suggestion +resharper_try_statements_can_be_merged_highlighting = hint +resharper_ts_not_resolved_highlighting = error +resharper_ts_resolved_from_inaccessible_module_highlighting = error +resharper_type_guard_doesnt_affect_anything_highlighting = warning +resharper_type_guard_produces_never_type_highlighting = warning +resharper_type_parameter_can_be_variant_highlighting = suggestion +resharper_type_parameter_hides_type_param_from_outer_scope_highlighting = warning +resharper_ul_tag_contains_non_li_elements_highlighting = hint +resharper_unassigned_field_global_highlighting = suggestion +resharper_unassigned_field_local_highlighting = warning +resharper_unassigned_get_only_auto_property_highlighting = warning +resharper_unassigned_readonly_field_highlighting = warning +resharper_unclosed_script_highlighting = error +resharper_undeclared_global_variable_using_highlighting = warning +resharper_unexpected_value_highlighting = error +resharper_unknown_css_class_highlighting = warning +resharper_unknown_css_variable_highlighting = warning +resharper_unknown_css_vendor_extension_highlighting = hint +resharper_unknown_item_group_highlighting = suggestion +resharper_unknown_metadata_highlighting = suggestion +resharper_unknown_output_parameter_highlighting = warning +resharper_unknown_property_highlighting = none +resharper_unknown_target_highlighting = suggestion +resharper_unknown_task_attribute_highlighting = suggestion +resharper_unknown_task_highlighting = warning +resharper_unnecessary_whitespace_highlighting = suggestion +resharper_unreachable_switch_arm_due_to_integer_analysis_highlighting = warning +resharper_unreachable_switch_case_due_to_integer_analysis_highlighting = warning +resharper_unreal_header_tool_error_highlighting = error +resharper_unreal_header_tool_warning_highlighting = warning +resharper_unsafe_comma_in_object_properties_list_highlighting = warning +resharper_unsupported_required_base_type_highlighting = warning +resharper_unused_anonymous_method_signature_highlighting = warning +resharper_unused_auto_property_accessor_global_highlighting = none +resharper_unused_auto_property_accessor_local_highlighting = warning +resharper_unused_import_clause_highlighting = warning +resharper_unused_inherited_parameter_highlighting = hint +resharper_unused_locals_highlighting = warning +resharper_unused_local_function_highlighting = warning +resharper_unused_local_function_parameter_highlighting = warning +resharper_unused_local_function_return_value_highlighting = warning +resharper_unused_local_import_highlighting = warning +resharper_unused_member_global_highlighting = none +resharper_unused_member_hierarchy_global_highlighting = suggestion +resharper_unused_member_hierarchy_local_highlighting = warning +resharper_unused_member_in_super_global_highlighting = suggestion +resharper_unused_member_in_super_local_highlighting = warning +resharper_unused_member_local_highlighting = warning +resharper_unused_method_return_value_global_highlighting = suggestion +resharper_unused_method_return_value_local_highlighting = warning +resharper_unused_nullable_directive_highlighting = warning +resharper_unused_parameter_global_highlighting = suggestion +resharper_unused_parameter_highlighting = warning +resharper_unused_parameter_in_partial_method_highlighting = warning +resharper_unused_parameter_local_highlighting = warning +resharper_unused_property_highlighting = warning +resharper_unused_tuple_component_in_return_value_highlighting = warning +resharper_unused_type_global_highlighting = none +resharper_unused_type_local_highlighting = warning +resharper_unused_type_parameter_highlighting = warning +resharper_unused_variable_highlighting = warning +resharper_usage_of_definitely_unassigned_value_highlighting = warning +resharper_usage_of_possibly_unassigned_value_highlighting = warning +resharper_useless_binary_operation_highlighting = warning +resharper_useless_comparison_to_integral_constant_highlighting = warning +resharper_use_array_creation_expression_1_highlighting = suggestion +resharper_use_array_creation_expression_2_highlighting = suggestion +resharper_use_array_empty_method_highlighting = suggestion +resharper_use_as_instead_of_type_cast_highlighting = hint +resharper_use_await_using_highlighting = suggestion +resharper_use_cancellation_token_for_i_async_enumerable_highlighting = suggestion +resharper_use_collection_count_property_highlighting = suggestion +resharper_use_configure_await_false_for_async_disposable_highlighting = none +resharper_use_configure_await_false_highlighting = suggestion +resharper_use_deconstruction_highlighting = hint +resharper_use_discard_assignment_highlighting = suggestion +resharper_use_empty_types_field_highlighting = suggestion +resharper_use_event_args_empty_field_highlighting = suggestion +resharper_use_format_specifier_in_format_string_highlighting = suggestion +resharper_use_implicitly_typed_variable_evident_highlighting = hint +resharper_use_implicitly_typed_variable_highlighting = none +resharper_use_implicit_by_val_modifier_highlighting = hint +resharper_use_indexed_property_highlighting = suggestion +resharper_use_index_from_end_expression_highlighting = suggestion +resharper_use_is_operator_1_highlighting = suggestion +resharper_use_is_operator_2_highlighting = suggestion +resharper_use_method_any_0_highlighting = suggestion +resharper_use_method_any_1_highlighting = suggestion +resharper_use_method_any_2_highlighting = suggestion +resharper_use_method_any_3_highlighting = suggestion +resharper_use_method_any_4_highlighting = suggestion +resharper_use_method_is_instance_of_type_highlighting = suggestion +resharper_use_nameof_expression_for_part_of_the_string_highlighting = none +resharper_use_nameof_expression_highlighting = suggestion +resharper_use_nameof_for_dependency_property_highlighting = suggestion +resharper_use_name_of_instead_of_type_of_highlighting = suggestion +resharper_use_negated_pattern_in_is_expression_highlighting = hint +resharper_use_negated_pattern_matching_highlighting = hint +resharper_use_nullable_annotation_instead_of_attribute_highlighting = suggestion +resharper_use_nullable_attributes_supported_by_compiler_highlighting = suggestion +resharper_use_nullable_reference_types_annotation_syntax_highlighting = warning +resharper_use_null_propagation_highlighting = hint +resharper_use_object_or_collection_initializer_highlighting = suggestion +resharper_use_of_implicit_global_in_function_scope_highlighting = warning +resharper_use_of_possibly_unassigned_property_highlighting = warning +resharper_use_pattern_matching_highlighting = suggestion +resharper_use_positional_deconstruction_pattern_highlighting = none +resharper_use_raw_string_highlighting = hint +resharper_use_string_interpolation_highlighting = suggestion +resharper_use_string_interpolation_when_possible_highlighting = hint +resharper_use_switch_case_pattern_variable_highlighting = suggestion +resharper_use_throw_if_null_method_highlighting = none +resharper_use_unsigned_right_shift_operator_highlighting = suggestion +resharper_use_verbatim_string_highlighting = hint +resharper_use_with_expression_to_copy_anonymous_object_highlighting = suggestion +resharper_use_with_expression_to_copy_record_highlighting = suggestion +resharper_use_with_expression_to_copy_struct_highlighting = suggestion +resharper_use_with_expression_to_copy_tuple_highlighting = suggestion +resharper_using_of_reserved_word_error_highlighting = error +resharper_using_of_reserved_word_highlighting = warning +resharper_using_statement_resource_initialization_expression_highlighting = hint +resharper_using_statement_resource_initialization_highlighting = warning +resharper_value_parameter_not_used_highlighting = warning +resharper_value_range_attribute_violation_highlighting = warning +resharper_value_should_have_units_highlighting = error +resharper_variable_can_be_made_const_highlighting = hint +resharper_variable_can_be_made_let_highlighting = hint +resharper_variable_can_be_moved_to_inner_block_highlighting = hint +resharper_variable_can_be_not_nullable_highlighting = warning +resharper_variable_hides_outer_variable_highlighting = warning +resharper_variable_used_before_declared_highlighting = warning +resharper_variable_used_in_inner_scope_before_declared_highlighting = warning +resharper_variable_used_out_of_scope_highlighting = warning +resharper_vb_check_for_reference_equality_instead_1_highlighting = suggestion +resharper_vb_check_for_reference_equality_instead_2_highlighting = suggestion +resharper_vb_possible_mistaken_argument_highlighting = warning +resharper_vb_possible_mistaken_call_to_get_type_1_highlighting = warning +resharper_vb_possible_mistaken_call_to_get_type_2_highlighting = warning +resharper_vb_remove_to_list_1_highlighting = suggestion +resharper_vb_remove_to_list_2_highlighting = suggestion +resharper_vb_replace_with_first_or_default_highlighting = suggestion +resharper_vb_replace_with_last_or_default_highlighting = suggestion +resharper_vb_replace_with_of_type_1_highlighting = suggestion +resharper_vb_replace_with_of_type_2_highlighting = suggestion +resharper_vb_replace_with_of_type_any_1_highlighting = suggestion +resharper_vb_replace_with_of_type_any_2_highlighting = suggestion +resharper_vb_replace_with_of_type_count_1_highlighting = suggestion +resharper_vb_replace_with_of_type_count_2_highlighting = suggestion +resharper_vb_replace_with_of_type_first_1_highlighting = suggestion +resharper_vb_replace_with_of_type_first_2_highlighting = suggestion +resharper_vb_replace_with_of_type_first_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_first_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_last_1_highlighting = suggestion +resharper_vb_replace_with_of_type_last_2_highlighting = suggestion +resharper_vb_replace_with_of_type_last_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_last_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_single_1_highlighting = suggestion +resharper_vb_replace_with_of_type_single_2_highlighting = suggestion +resharper_vb_replace_with_of_type_single_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_single_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_where_highlighting = suggestion +resharper_vb_replace_with_single_assignment_1_highlighting = suggestion +resharper_vb_replace_with_single_assignment_2_highlighting = suggestion +resharper_vb_replace_with_single_call_to_any_highlighting = suggestion +resharper_vb_replace_with_single_call_to_count_highlighting = suggestion +resharper_vb_replace_with_single_call_to_first_highlighting = suggestion +resharper_vb_replace_with_single_call_to_first_or_default_highlighting = suggestion +resharper_vb_replace_with_single_call_to_last_highlighting = suggestion +resharper_vb_replace_with_single_call_to_last_or_default_highlighting = suggestion +resharper_vb_replace_with_single_call_to_single_highlighting = suggestion +resharper_vb_replace_with_single_call_to_single_or_default_highlighting = suggestion +resharper_vb_replace_with_single_or_default_highlighting = suggestion +resharper_vb_simplify_linq_expression_10_highlighting = hint +resharper_vb_simplify_linq_expression_1_highlighting = suggestion +resharper_vb_simplify_linq_expression_2_highlighting = suggestion +resharper_vb_simplify_linq_expression_3_highlighting = suggestion +resharper_vb_simplify_linq_expression_4_highlighting = suggestion +resharper_vb_simplify_linq_expression_5_highlighting = suggestion +resharper_vb_simplify_linq_expression_6_highlighting = suggestion +resharper_vb_simplify_linq_expression_7_highlighting = hint +resharper_vb_simplify_linq_expression_8_highlighting = hint +resharper_vb_simplify_linq_expression_9_highlighting = hint +resharper_vb_string_compare_is_culture_specific_1_highlighting = warning +resharper_vb_string_compare_is_culture_specific_2_highlighting = warning +resharper_vb_string_compare_is_culture_specific_3_highlighting = warning +resharper_vb_string_compare_is_culture_specific_4_highlighting = warning +resharper_vb_string_compare_is_culture_specific_5_highlighting = warning +resharper_vb_string_compare_is_culture_specific_6_highlighting = warning +resharper_vb_string_compare_to_is_culture_specific_highlighting = warning +resharper_vb_string_ends_with_is_culture_specific_highlighting = none +resharper_vb_string_index_of_is_culture_specific_1_highlighting = warning +resharper_vb_string_index_of_is_culture_specific_2_highlighting = warning +resharper_vb_string_index_of_is_culture_specific_3_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_1_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_2_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_3_highlighting = warning +resharper_vb_string_starts_with_is_culture_specific_highlighting = none +resharper_vb_unreachable_code_highlighting = warning +resharper_vb_use_array_creation_expression_1_highlighting = suggestion +resharper_vb_use_array_creation_expression_2_highlighting = suggestion +resharper_vb_use_first_instead_highlighting = warning +resharper_vb_use_method_any_1_highlighting = suggestion +resharper_vb_use_method_any_2_highlighting = suggestion +resharper_vb_use_method_any_3_highlighting = suggestion +resharper_vb_use_method_any_4_highlighting = suggestion +resharper_vb_use_method_any_5_highlighting = suggestion +resharper_vb_use_method_is_instance_of_type_highlighting = suggestion +resharper_vb_use_type_of_is_operator_1_highlighting = suggestion +resharper_vb_use_type_of_is_operator_2_highlighting = suggestion +resharper_virtual_member_call_in_constructor_highlighting = none +resharper_virtual_member_never_overridden_global_highlighting = suggestion +resharper_virtual_member_never_overridden_local_highlighting = suggestion +resharper_void_method_with_must_use_return_value_attribute_highlighting = warning +resharper_vulnerable_package_highlighting = warning +resharper_web_config_module_not_resolved_highlighting = error +resharper_web_config_module_qualification_resolve_highlighting = warning +resharper_web_config_redundant_add_namespace_tag_highlighting = warning +resharper_web_config_redundant_location_tag_highlighting = warning +resharper_web_config_tag_prefix_redundand_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = error +resharper_web_config_unused_add_tag_highlighting = warning +resharper_web_config_unused_element_due_to_config_source_attribute_highlighting = warning +resharper_web_config_unused_remove_or_clear_tag_highlighting = warning +resharper_web_config_web_config_path_warning_highlighting = warning +resharper_web_config_wrong_module_highlighting = error +resharper_web_ignored_path_highlighting = none +resharper_web_mapped_path_highlighting = hint +resharper_with_expression_instead_of_initializer_highlighting = suggestion +resharper_with_expression_modifies_all_members_highlighting = warning +resharper_with_statement_using_error_highlighting = error +resharper_wrong_expression_statement_highlighting = warning +resharper_wrong_indent_size_highlighting = warning +resharper_wrong_metadata_use_highlighting = none +resharper_wrong_public_modifier_specification_highlighting = hint +resharper_wrong_require_relative_path_highlighting = hint +resharper_xaml_assign_null_to_not_null_attribute_highlighting = warning +resharper_xaml_avalonia_wrong_binding_mode_for_stream_binding_operator_highlighting = warning +resharper_xaml_binding_without_context_not_resolved_highlighting = hint +resharper_xaml_binding_with_context_not_resolved_highlighting = warning +resharper_xaml_compiled_binding_missing_data_type_error_highlighting_highlighting = error +resharper_xaml_constructor_warning_highlighting = warning +resharper_xaml_decimal_parsing_is_culture_dependent_highlighting = warning +resharper_xaml_dependency_property_resolve_error_highlighting = warning +resharper_xaml_duplicate_style_setter_highlighting = warning +resharper_xaml_dynamic_resource_error_highlighting = error +resharper_xaml_element_name_reference_not_resolved_highlighting = error +resharper_xaml_empty_grid_length_definition_highlighting = error +resharper_xaml_field_modifier_requires_name_attribute_highlighting = warning +resharper_xaml_grid_definitions_can_be_converted_to_attribute_highlighting = hint +resharper_xaml_ignored_path_highlighting_highlighting = none +resharper_xaml_index_out_of_grid_definition_highlighting = warning +resharper_xaml_invalid_member_type_highlighting = error +resharper_xaml_invalid_resource_target_type_highlighting = error +resharper_xaml_invalid_resource_type_highlighting = error +resharper_xaml_invalid_type_highlighting = error +resharper_xaml_language_level_highlighting = error +resharper_xaml_mapped_path_highlighting_highlighting = hint +resharper_xaml_method_arguments_will_be_ignored_highlighting = warning +resharper_xaml_missing_grid_index_highlighting = warning +resharper_xaml_overloads_collision_highlighting = warning +resharper_xaml_parent_is_out_of_current_component_tree_highlighting = warning +resharper_xaml_path_error_highlighting = warning +resharper_xaml_possible_null_reference_exception_highlighting = suggestion +resharper_xaml_redundant_attached_property_highlighting = warning +resharper_xaml_redundant_binding_mode_attribute_highlighting = warning +resharper_xaml_redundant_collection_property_highlighting = warning +resharper_xaml_redundant_freeze_attribute_highlighting = warning +resharper_xaml_redundant_grid_definitions_highlighting = warning +resharper_xaml_redundant_grid_span_highlighting = warning +resharper_xaml_redundant_modifiers_attribute_highlighting = warning +resharper_xaml_redundant_namespace_alias_highlighting = warning +resharper_xaml_redundant_name_attribute_highlighting = warning +resharper_xaml_redundant_property_type_qualifier_highlighting = warning +resharper_xaml_redundant_resource_highlighting = warning +resharper_xaml_redundant_styled_value_highlighting = warning +resharper_xaml_redundant_update_source_trigger_attribute_highlighting = warning +resharper_xaml_redundant_xamarin_forms_class_declaration_highlighting = warning +resharper_xaml_resource_file_path_case_mismatch_highlighting = warning +resharper_xaml_routed_event_resolve_error_highlighting = warning +resharper_xaml_static_resource_not_resolved_highlighting = warning +resharper_xaml_style_class_not_found_highlighting = warning +resharper_xaml_style_invalid_target_type_highlighting = error +resharper_xaml_unexpected_element_highlighting = error +resharper_xaml_unexpected_text_token_highlighting = error +resharper_xaml_xaml_duplicate_device_family_type_view_highlighting_highlighting = error +resharper_xaml_xaml_mismatched_device_family_view_clr_name_highlighting_highlighting = warning +resharper_xaml_xaml_relative_source_default_mode_warning_highlighting_highlighting = warning +resharper_xaml_xaml_unknown_device_family_type_highlighting_highlighting = warning +resharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_highlighting_highlighting = warning +resharper_xaml_x_key_attribute_disallowed_highlighting = error +resharper_xml_doc_comment_syntax_problem_highlighting = warning +resharper_xunit_xunit_test_with_console_output_highlighting = warning +resharper_zero_index_from_end_highlighting = warning + +[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cppm,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,ixx,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = tab +indent_size = tab +tab_width = 4 diff --git a/MikuSharp/Attributes/CommandAttributes.cs b/MikuSharp/Attributes/CommandAttributes.cs new file mode 100644 index 00000000..f77d1ddf --- /dev/null +++ b/MikuSharp/Attributes/CommandAttributes.cs @@ -0,0 +1,162 @@ +using MikuSharp.Enums; +using MikuSharp.Utilities; + +namespace MikuSharp.Attributes; + +/// +/// Defines that usage of this command is restricted to users in a vc. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var connected = ctx.Member?.VoiceState?.Channel is not null; + + if (connected) + return true; + + await ctx.EditResponseAsync("You must be in a voice channel to use this command."); + return false; + } +} + +/// +/// Defines that usage of this command is restricted to users in a vc and the bot is in a vc. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.Guild); + var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); + var connected = ctx.Member?.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null; + if (connected) + return true; + + await ctx.EditResponseAsync("You and the bot must be in a voice channel to use this command."); + return false; + } +} + +/// +/// Defines that usage of this command is forbidden for discord staff. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, Inherited = false)] +public sealed class NotDiscordStaffAttribute : CheckBaseAttribute +{ + /// + public override Task ExecuteCheckAsync(CommandContext ctx, bool help) + => Task.FromResult(!ctx.User.IsStaff); +} + +/// +/// Defines that the method or class will defer the response. +/// +/// Whether the response should be epehemeral. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class DeferResponseAsyncAttribute(bool ephemeral = false) : ApplicationCommandCheckBaseAttribute +{ + /// + /// Gets a value indicating whether the response should be ephemeral. + /// + public bool Ephemeral { get; } = ephemeral; + + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + if (this.Ephemeral) + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + else + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + + return true; + } +} + +/// +/// Defines that the method or class needs to ensure that a lavalink session is available. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class EnsureLavalinkSession : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var module = ctx.Client.GetLavalink(); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (module is null) + return await RespondWithNoSessionAvailableAsync(ctx); + + var session = module.DefaultSession(); + return (session is not null && session.IsConnected) || await RespondWithNoSessionAvailableAsync(ctx); + } + + /// + /// Responds with a message indicating that no session is available. + /// + /// The context. + /// . + public static async Task RespondWithNoSessionAvailableAsync(BaseContext ctx) + { + await ctx.EditResponseAsync("No session found that can handle music at the moment."); + return false; + } +} + +/// +/// Defines that the method or class will automatically disconnect an existing session. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class AutomaticallyDisconnectExistingSessionAttribute : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + ArgumentNullException.ThrowIfNull(ctx.Guild); + var guildId = ctx.GuildId.Value; + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) + { + if (!HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out _)) + return true; + + var player = ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild); + if (player is not null) + await player.DisconnectAsync(); + HatsuneMikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out _); + } + + return true; + } +} + +/// +/// Defines that the method or class requires specific playback state(s). +/// +/// The target playback states. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class RequirePlaybackState(params PlaybackState[] targetStates) : ApplicationCommandCheckBaseAttribute +{ + /// + /// Gets the target playback state. + /// + public List TargetStates { get; } = [..targetStates]; + + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var guildId = ctx.GuildId.Value; + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) + { + return HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession) && this.TargetStates.Contains(musicSession.PlaybackState); + } + } +} diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs deleted file mode 100644 index 0db8c3ae..00000000 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ /dev/null @@ -1,49 +0,0 @@ -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; - -using System; -using System.Threading.Tasks; - -namespace MikuSharp.Attributes; - -/// -/// Defines that usage of this command is restricted to users in a vc. -/// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBaseAttribute -{ - public override Task ExecuteChecksAsync(BaseContext ctx) - { - if (ctx.Member.VoiceState?.Channel != null) - return Task.FromResult(true); - - return Task.FromResult(false); - } -} - -/// -/// Defines that usage of this command is restricted to users & the bot in a vc. -/// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute -{ - public override async Task ExecuteChecksAsync(BaseContext ctx) - { - var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); - if (ctx.Member.VoiceState?.Channel != null && bot.VoiceState?.Channel != null) - return await Task.FromResult(true); - - return await Task.FromResult(false); - } -} - -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, AllowMultiple = false)] -public sealed class NotStaffAttribute : CheckBaseAttribute -{ - public override Task ExecuteCheckAsync(CommandContext ctx, bool help) - { - return Task.FromResult(!ctx.User.IsStaff); - } -} \ No newline at end of file diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs deleted file mode 100644 index fbac0b12..00000000 --- a/MikuSharp/Commands/About.cs +++ /dev/null @@ -1,167 +0,0 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity.Extensions; - -using Google.Apis.YouTube.v3.Data; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("about", "About")] -internal class About : ApplicationCommandsModule -{ - [SlashCommand("donate", "Financial support information")] - public static async Task DonateAsync(InteractionContext ctx) - { - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl). - WithTitle("Donate Page!"). - WithAuthor("Miku MikuBot uwu"). - WithUrl("https://meek.moe/"). - WithColor(new DiscordColor("#348573")). - WithDescription("Thank you for your interest in supporting the bot's development!\n" + - "Here are some links that may interest you"). - AddField(new DiscordEmbedField("Patreon", "[Link](https://patreon.com/sekoree)", true)). - AddField(new DiscordEmbedField("PayPal", "[Link](https://paypal.me/speyd3r)", true)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); - } - - [SlashCommand("bot", "About the bot")] - public static async Task BotAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl). - WithTitle($"About {ctx.Client.CurrentUser.UsernameWithDiscriminator}!"). - WithAuthor("Miku MikuBot uwu"). - WithUrl("https://meek.moe/"). - WithColor(new DiscordColor("#348573")). - WithDescription(ctx.Client.CurrentApplication.Description); - foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new DiscordEmbedField(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithDiscriminator)); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] - public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != ctx.Guild.OwnerId) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); - var f = await announcementChannel.FollowAsync(channel); - await Task.Delay(5000); - var msgs = await channel.GetMessagesAsync(); - var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); - await target.DeleteAsync("Message cleanup"); - var webhooks = await channel.GetWebhooksAsync(); - var webhook = webhooks.First(x => x.Id == f.WebhookId); - var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; - var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); - var memoryStream = new System.IO.MemoryStream(); - await stream.CopyToAsync(memoryStream); - memoryStream.Position = 0; - await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"News setup complete {DiscordEmoji.FromGuildEmote(client: MikuBot.ShardedClient.GetShard(483279257431441410), id: 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); - } - - [SlashCommand("feedback", "Send feedback!")] - public static async Task FeedbackAsync(InteractionContext ctx) - { - DiscordInteractionModalBuilder modalBuilder = new(); - modalBuilder.WithTitle("Feedback modal"); - modalBuilder.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); - modalBuilder.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20, null, true, null)); - await ctx.CreateModalResponseAsync(modalBuilder); - - var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); - if (!res.TimedOut) - { - await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; - var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(id: 483279257431441410); - var emb = new DiscordEmbedBuilder(); - emb.WithAuthor($"{ctx.User.UsernameWithDiscriminator}", iconUrl: ctx.User.AvatarUrl). - WithTitle(title). - WithDescription(body); - if (ctx.Guild != null) - emb.AddField(new DiscordEmbedField("Guild", $"{ctx.Guild.Id}", true)); - var forum = guild.GetChannel(1020433162662322257); - List tags = new(); - if (ctx.Guild != null) - tags.Add(forum.AvailableTags.First(x => x.Id == 1020434799493648404)); - else - tags.Add(forum.AvailableTags.First(x => x.Id == 1020434935502360576)); - var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithDiscriminator}"), null, tags, "Feedback"); - var msg = await thread.GetMessageAsync(thread.Id); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); - await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); - - } - else - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"You were too slow :(\nThe time limit is one minute.").AsEphemeral()); - - } - - [SlashCommand("ping", "Current ping to discord's services")] - public static async Task PingAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); - - [SlashCommand("which_shard", "What shard am I on?")] - public static async Task GetExecutingShardAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); - - [SlashCommand("stats", "Some stats of the MikuBot!")] - public static async Task StatsAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - int GuildCount = 0; - int UserCount = 0; - int ChannelCount = 0; - foreach (var client in MikuBot.ShardedClient.ShardClients) - { - GuildCount += client.Value.Guilds.Count; - foreach (var guild in client.Value.Guilds) - { - UserCount += guild.Value.MemberCount; - ChannelCount += guild.Value.Channels.Count; - } - } - var emb = new DiscordEmbedBuilder(). - WithTitle("Stats"). - WithDescription("Some stats of the MikuBot!"). - AddField(new DiscordEmbedField("Guilds", GuildCount.ToString(), true)). - AddField(new DiscordEmbedField("Users", UserCount.ToString(), true)). - AddField(new DiscordEmbedField("Channels", ChannelCount.ToString(), true)). - AddField(new DiscordEmbedField("Ping", ctx.Client.Ping.ToString(), true)). - AddField(new DiscordEmbedField("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)). - WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("support", "Link to my support server")] - public static async Task SupportAsybc(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(id: 483279257431441410); - var widget = await guild.GetWidgetAsync(); - var emb = new DiscordEmbedBuilder(). - WithTitle("Support Server"). - WithDescription("Need help or is something broken?"). - WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new DiscordComponentEmoji(704733597655105634)))); - } -} diff --git a/MikuSharp/Commands/AboutCommands.cs b/MikuSharp/Commands/AboutCommands.cs new file mode 100644 index 00000000..b16e590d --- /dev/null +++ b/MikuSharp/Commands/AboutCommands.cs @@ -0,0 +1,161 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("about", "About")] +internal class AboutCommands : ApplicationCommandsModule +{ + [SlashCommand("donate", "Financial support information")] + public static async Task DonateAsync(InteractionContext ctx) + { + DiscordContainerComponent container = new(accentColor: DiscordColor.Gold); + DiscordSectionComponent section = new(); + section.WithThumbnailComponent("https://i.imgur.com/HyqWCep.png"); + section.AddTextDisplayComponent(new("Donations".Header1())); + section.AddTextDisplayComponent(new("Thank you for your interest in supporting the bot's development ❤️\nHere are some links that may interest you:")); + section.AddTextDisplayComponent(new("Creator (@sekoree)".Header3() + "\n" + + "- " + "Patreon".MaskedUrl(new("https://patreon.com/sekoree")) + "\n" + + "- " + "PayPal".MaskedUrl(new("https://paypal.me/speyd3r")) + "\n" + + "Current Developer (@lulalaby)".Header3() + "\n" + + "- " + "PayPal".MaskedUrl(new("https://paypal.me/aitsys")) + "\n" + + "- " + "GitHub Sponsors".MaskedUrl(new("https://github.com/sponsors/Lulalaby")) + "\n\n" + + "Many thanks!".Subtext())); + container.AddComponent(section); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(container)); + } + + [SlashCommand("bot", "Information about the bot"), DeferResponseAsync(true)] + public static async Task BotAsync(InteractionContext ctx) + { + DiscordContainerComponent container = new(accentColor: new("#348573")); + DiscordSectionComponent section = new(); + section.WithThumbnailComponent("https://i.imgur.com/Uew8VFr.png"); + section.AddTextDisplayComponent(new($"About {ctx.Client.CurrentApplication.Name}".Header1())); + if (ctx.Client.CurrentApplication.Description is not null) + section.AddTextDisplayComponent(new(ctx.Client.CurrentApplication.Description)); + if (ctx.Client.CurrentApplication.Team is not null) + section.AddTextDisplayComponent(new("Team".Header3() + "\n" + string.Join("\n", ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username).Select(m => $"- {m.User.Username.Bold()} ({m.Role})")))); + else + section.AddTextDisplayComponent(new("Owner".Header3() + "\n" + ctx.Client.CurrentApplication.Owner.UsernameWithGlobalName)); + container.AddComponent(section); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(container)); + } + + // TODO: CV2 + [SlashCommand("news", "Get news about the bot in your server", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true)] + public static async Task FollowNewsAsync( + InteractionContext ctx, + [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] + DiscordChannel channel, + [Option("name", "Name of webhook")] string name = "Miku Bot Announcements" + ) + { + var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); + var f = await announcementChannel.FollowAsync(channel); + await Task.Delay(5000); + var msgs = await channel.GetMessagesAsync(); + var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); + await target.DeleteAsync("Message cleanup"); + var webhooks = await channel.GetWebhooksAsync(); + var webhook = webhooks.First(x => x.Id == f.WebhookId); + var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; + var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( + $"News setup complete {DiscordEmoji.FromGuildEmote(HatsuneMikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); + } + + // TODO: CV2 + [SlashCommand("feedback", "Send feedback to the developers")] + public static async Task FeedbackAsync(InteractionContext ctx) + { + DiscordInteractionModalBuilder modalBuilder = new(); + modalBuilder.WithTitle("Feedback modal"); + modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "Title of feedback", "feedbacktitle", null, 5, null, true, "Feedback")); + modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "Your feedback", "feedbackbody", null, 20)); + await ctx.CreateModalResponseAsync(modalBuilder); + + var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(2)); + + if (!res.TimedOut) + { + await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var title = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbacktitle").Value; + var body = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbackbody").Value; + //var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var emb = new DiscordEmbedBuilder(); + emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); + if (ctx.Guild is not null) + emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); + var forum = await ctx.Client.GetChannelAsync(1020433162662322257, true); + List tags = + [ + ctx.Guild is not null + ? forum.AvailableTags.First(x => x.Id is 1020434799493648404) + : forum.AvailableTags.First(x => x.Id is 1020434935502360576) + ]; + var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); + var msg = await thread.GetMessageAsync(thread.Id); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); + await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(HatsuneMikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); + } + else + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is two minutes.").AsEphemeral()); + } + + [SlashCommand("ping", "Current ping to discord's services")] + public static async Task PingAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(new DiscordTextDisplayComponent($"Ping: {$"{ctx.Client.Ping}ms".InlineCode()}"))); + + [SlashCommand("which_shard", "Gets the id of current shard you're using me on")] + public static async Task GetExecutingShardAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(new DiscordTextDisplayComponent($"Shard: {ctx.Client.ShardId.ToString().InlineCode()}"))); + + // TODO: CV2 + [SlashCommand("stats", "Statistics about the bot!"), DeferResponseAsync(true)] + public static async Task StatsAsync(InteractionContext ctx) + { + var statistics = HatsuneMikuBot.ShardedClient.Statistics; + + var knownGuildFeatures = HatsuneMikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); + + var averagePing = (int)HatsuneMikuBot.ShardedClient.ShardClients.Values.Average(client => client.Ping); + + DiscordEmbedBuilder builder = new(); + builder.WithTitle("Stats"); + builder.WithDescription($"Some stats about {ctx.Client.CurrentApplication.Name}!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); + foreach (var (key, value) in statistics) + builder.AddField(new(key.ToString(), value.ToString().InlineCode(), true)); + builder.AddField(new("Ping", $"{averagePing}ms".InlineCode(), true)); + if (ctx.Client.VersionString.Contains('+')) + builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/{ctx.Client.VersionString.Split('+').Last()}")), true)); + else + builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/v{ctx.Client.VersionString.Trim()}")), true)); + builder.AddField(new("API Channel (Discord)", ctx.Client.ApiChannel.ToString().InlineCode(), true)); + builder.AddField(new("API Version (Discord)", ctx.Client.ApiVersion.InlineCode(), true)); + var lavalinkDefaultSession = ctx.Client.GetLavalink()?.DefaultSession(); + if (lavalinkDefaultSession is not null) + builder.AddField(new("Lavalink Version", $"{await lavalinkDefaultSession.GetLavalinkVersionAsync()}".InlineCode(), true)); + builder.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + if (ctx.Client.CurrentUser.BannerUrl is not null) + builder.WithImageUrl(ctx.Client.CurrentUser.BannerUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); + } + + [SlashCommand("support", "Link to the support server"), DeferResponseAsync(true)] + public static async Task SupportAsybc(InteractionContext ctx) + { + var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var widget = await guild.GetWidgetAsync(); + DiscordContainerComponent container = new(accentColor: DiscordColor.IndianRed); + container.AddComponent(new DiscordTextDisplayComponent("Support".Header1())); + container.AddComponent(new DiscordTextDisplayComponent("Need help or is something broken?")); + container.AddComponent(new DiscordActionRowComponent([new DiscordLinkButtonComponent(widget.InstantInviteUrl.Replace("canary.discord.com/invite", "discord.gg"), "Support Server", false, new(704733597655105634))])); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(container)); + } +} diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs deleted file mode 100644 index 1dd6ad65..00000000 --- a/MikuSharp/Commands/Action.cs +++ /dev/null @@ -1,108 +0,0 @@ -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using HeyRed.Mime; - -using MikuSharp.Utilities; - -using System.IO; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("action", "Actions", dmPermission: false)] -internal class Action : ApplicationCommandsModule -{ - [SlashCommand("hug", "Hug someone!")] - public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("kiss", "Kiss someone!")] - public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("lick", "Lick someone!")] - public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("pat", "Pat someone!")] - public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var weeurl = await MikuBot._weebClient.GetRandomAsync("pat", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("poke", "Poke someone!")] - public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var weeurl = await MikuBot._weebClient.GetRandomAsync("poke", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("slap", "Slap someone!")] - public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var weeurl = await MikuBot._weebClient.GetRandomAsync("slap", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } -} diff --git a/MikuSharp/Commands/ActionCommands.cs b/MikuSharp/Commands/ActionCommands.cs new file mode 100644 index 00000000..4f088ee3 --- /dev/null +++ b/MikuSharp/Commands/ActionCommands.cs @@ -0,0 +1,134 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("action", "Actions", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] +internal class ActionCommands : ApplicationCommandsModule +{ + [SlashCommand("hug", "Hug someone!")] + public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser? user = null) + { + var title = "## A wild hug appears!"; + var content = $"{ctx.User.Mention} hugs {user?.Mention ?? "everyone"} uwu"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("hug")).TryGetWeebShImage(out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("kiss", "Kiss someone!")] + public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## A kiss!"; + var content = $"{ctx.User.Mention} kisses {user.Mention} >~<"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("kiss")).TryGetWeebShImage(out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("lick", "Lick someone!")] + public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Slurp~"; + var content = $"{ctx.User.Mention} licks {user.Mention} owo"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("lick")).TryGetWeebShImage(out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("pat", "Pat someone!")] + public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Pat pat~"; + var content = $"{ctx.User.Mention} pats {user.Mention} #w#"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("poke", "Poke someone!")] + public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Poke poke!"; + var content = $"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("slap", "Slap someone!")] + public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Slap!"; + var content = $"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("bite", "Bite someone!")] + public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Bite >:3"; + var content = $"{ctx.User.Mention} bites {user.Mention} >:3"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("nom", "Nom someone!")] + public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Nom nom~"; + var content = $"{ctx.User.Mention} noms {user.Mention} >:3c"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } + + [SlashCommand("stare", "Stare at someone!")] + public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + var title = "## Stare O.o"; + var content = $"{ctx.User.Mention} stares at {user.Mention} O.o"; + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) + { + await ctx.ActionRespondWithErrorAsync(content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); + } +} diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs deleted file mode 100644 index 8894d6b3..00000000 --- a/MikuSharp/Commands/Developer.cs +++ /dev/null @@ -1,228 +0,0 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; - -using Microsoft.CodeAnalysis.CSharp.Scripting; -using Microsoft.CodeAnalysis.Scripting; - -using MikuSharp.Entities; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -/// -/// The developer commands. -/// -public class Developer : ApplicationCommandsModule -{ - [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - } - - [SlashCommand("guild_shard_test", "Testing")] - public static async Task GuildTestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); - } - } - - [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public static async Task DeleteMessageAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - await ctx.TargetMessage.DeleteAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } - - /// - /// Gets the debug log. - /// - /// The interaction context. - [SlashCommand("dbg", "Get the logs of today")] - public static async Task GetDebugLogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); - DateTime now = DateTime.Now; - var target_file = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; - if (!File.Exists(target_file)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); - return; - } - else - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {Formatter.Bold(target_file)}")); - } - try - { - if (!File.Exists($"temp-{target_file}")) - { - File.Copy(target_file, $"temp-{target_file}"); - } - else - { - File.Delete($"temp-{target_file}"); - File.Copy(target_file, $"temp-{target_file}"); - } - FileStream log = new(path: $"temp-{target_file}", FileMode.Open, FileAccess.Read); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(target_file, log, true).WithContent($"Log {Formatter.Bold(target_file)}").AsEphemeral()); - log.Close(); - log.Dispose(); - File.Delete($"temp-{target_file}"); - } - catch (Exception ex) - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } - - /// - /// Evals the csharp script. - /// - /// The context menu context. - [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public static async Task EvalCSAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - var msg = ctx.TargetMessage; - var code = ctx.TargetMessage.Content; - var cs1 = code.IndexOf("```") + 3; - cs1 = code.IndexOf('\n', cs1) + 1; - var cs2 = code.LastIndexOf("```"); - var c = await ctx.Guild.GetActiveThreadsAsync(); - - if (cs1 == -1 || cs2 == -1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); - return; - } - - string cs = code[cs1..cs2]; - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithColor(new DiscordColor("#FF007F")) - .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") - .Build())).ConfigureAwait(false); - msg = await ctx.GetOriginalResponseAsync(); - try - { - var globals = new SGTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); - - var sopts = ScriptOptions.Default; - sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities", "DisCatSharp.Lavalink"); - sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); - - var script = CSharpScript.Create(cs, sopts, typeof(SGTestVariables)); - script.Compile(); - var result = await script.RunAsync(globals).ConfigureAwait(false); - - if (result != null && result.ReturnValue != null && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Result", Description = result.ReturnValue.ToString(), Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Successful", Description = "No result was returned.", Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); - } - catch (Exception ex) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Failure", Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), Color = new DiscordColor("#FF0000") }.Build())).ConfigureAwait(false); - } - } -} - -/// -/// The test variables. -/// -public class SGTestVariables -{ - /// - /// Gets or sets the message. - /// - public DiscordMessage Message { get; set; } - - public InteractivityExtension Inter { get; set; } - - /// - /// Gets or sets the channel. - /// - public DiscordChannel Channel { get; set; } - - /// - /// Gets or sets the guild. - /// - public DiscordGuild Guild { get; set; } - - /// - /// Gets or sets the user. - /// - public DiscordUser User { get; set; } - - /// - /// Gets or sets the member. - /// - public DiscordMember Member { get; set; } - - /// - /// Gets or sets the context menu context. - /// - public ContextMenuContext Context { get; set; } - - //public Dictionary Bot = MikuBot.Guilds; - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The client. - /// The context menu context. - public SGTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) - { - Client = client; - ShardClient = shard; - - Message = msg; - Channel = ctx.Channel; - Guild = ctx.Guild; - User = ctx.User; - Member = ctx.Member; - Context = ctx; - Inter = Client.GetInteractivity(); - } - - public DiscordShardedClient ShardClient { get; set; } - - public DiscordClient Client { get; set; } -} diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs new file mode 100644 index 00000000..1dad017f --- /dev/null +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -0,0 +1,408 @@ +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands; + +/// +/// The developer commands. +/// +[ApplicationCommandRequireTeamMember] +public class DeveloperOnlyCommands : ApplicationCommandsModule +{ + /// + /// The units. + /// + private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; + + /// + /// Evals a csharp script. + /// + /// The context menu context. + [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev"), DeferResponseAsync(true)] + public static async Task EvalAsync(ContextMenuContext ctx) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Eval request")); + var msg = ctx.TargetMessage; + var code = msg.Content; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; + cs1 = code.IndexOf('\n', cs1) + 1; + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); + + if (cs1 is -1 || cs2 is -1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); + return; + } + + var cs = code[cs1..cs2]; + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() + .WithColor(new("#FF007F")) + .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") + .Build())).ConfigureAwait(false); + + try + { + var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, HatsuneMikuBot.ShardedClient); + + var sopts = ScriptOptions.Default; + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", + "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); + sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + + var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); + script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); + + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Result", + Description = result.ReturnValue.ToString(), + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Successful", + Description = "No result was returned.", + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + } + catch (Exception ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Failure", + Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), + Color = new DiscordColor("#FF0000") + }.Build())).ConfigureAwait(false); + } + } + + /// + /// Evals a csharp script. Version 2 with the new components :eyes:. + /// + /// The context menu context. + [ContextMenu(ApplicationCommandType.Message, "Eval V2 - Miku Dev"), DeferResponseAsync] + public static async Task EvalV2Async(ContextMenuContext ctx) + { + DiscordWebhookBuilder responseBuilder = new(); + responseBuilder.WithV2Components(); + var msg = ctx.TargetMessage; + var code = msg.Content; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; + cs1 = code.IndexOf('\n', cs1) + 1; + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); + + if (cs1 is -1 || cs2 is -1) + { + await ctx.EditResponseAsync(responseBuilder.AddComponents(new DiscordTextDisplayComponent("You need to wrap the code into a code block."))); + return; + } + + var cs = code[cs1..cs2]; + DiscordTextDisplayComponent titleComponent = new("Code Evaluation".Header1()); + DiscordMediaGalleryComponent imageComponent = new([new("https://eval-deez-nuts.xyz/static/opinion.png")]); + DiscordTextDisplayComponent sourceTitleComponent = new("Source Code".Header2()); + DiscordTextDisplayComponent sourceComponent = new(cs.BlockCode("cs")); + + DiscordContainerComponent containerComponent = new(accentColor: DiscordColor.White); + DiscordTextDisplayComponent statusTitleComponent = new("Status".Header2()); + DiscordTextDisplayComponent stateComponent = new("Evaluating..."); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + + try + { + var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, HatsuneMikuBot.ShardedClient); + + var sopts = ScriptOptions.Default; + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", + "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); + sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + + await Task.Delay(TimeSpan.FromSeconds(2)); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Blue); + stateComponent = new("Creating Script.."); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); + + await Task.Delay(TimeSpan.FromSeconds(2)); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Yellow); + stateComponent = new("Compiling Script.."); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + + await Task.Delay(TimeSpan.FromSeconds(2)); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.SpringGreen); + stateComponent = new("Running Script.."); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); + + await Task.Delay(TimeSpan.FromSeconds(2)); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Green); + stateComponent = new("Evaluation Successful"); + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + { + DiscordTextDisplayComponent returnValueComponent = new("Return Value".Header2() + "\n" + result.ReturnValue.ToString()!); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent, returnValueComponent]); + } + else + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + } + catch (Exception ex) + { + await Task.Delay(TimeSpan.FromSeconds(2)); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.DarkRed); + stateComponent = new(":warning: Evaluation Failure"); + DiscordTextDisplayComponent exceptionComponent = new("Exception".Header2() + "\n" + string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message)); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent, exceptionComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); + } + } + + /// + /// Deletes a message sent by the bot. + /// + /// The context menu context. + [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev"), DeferResponseAsync(true)] + public static async Task DeleteMessageAsync(ContextMenuContext ctx) + { + if (ctx.TargetMessage.Author.Id != ctx.Client.CurrentUser.Id) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You can only delete messages sent by me.")); + return; + } + + await ctx.TargetMessage.DeleteAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } + + /// + /// Converts a size to a human-readable string. + /// + /// The size to convert. + /// A human-readable string representing the size. + private static string SizeToString(long size) + { + double convertedSize = size; + var u = 0; + + while (convertedSize >= 900 && u < s_units.Length - 2) + { + u++; + convertedSize /= 1024; + } + + return $"{convertedSize:#,##0.00} {s_units[u]}B"; + } + + /// + /// The developer commands. + /// + [SlashCommandGroup("dev", "Developer commands")] + public class DeveloperCommands : ApplicationCommandsModule + { + /// + /// A test command. + /// + /// The interaction context. + [SlashCommand("test", "Testing")] + public static async Task TestAsync(InteractionContext ctx) + { + var builder = new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage().SuppressEmbeds().AsVoiceMessage().WithV2Components(); + var response = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); + //await ctx.EditResponseAsync($"Send the following flags: {response.SendFlags}\nReceived the following flags from the callback response: {response.Message?.Flags}"); + /*await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])])));*/ + } + + /// + /// Guild shard test command. + /// + /// The interaction context. + [SlashCommand("guild_shard_test", "Testing")] + public static async Task GuildTestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + foreach (var shard in HatsuneMikuBot.ShardedClient.ShardClients.Values) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); + } + + /// + /// Gets the Lavalink statistics. + /// + /// The interaction context. + [SlashCommand("lstats", "Displays Lavalink statistics"), DeferResponseAsync(true)] + public static async Task GetLavalinkStatsAsync(InteractionContext ctx) + { + var stats = ctx.Client.GetLavalink().ConnectedSessions.First().Value.Statistics; + var sb = new StringBuilder(); + sb.Append("Lavalink resources usage statistics: ```") + .Append("Uptime: ").Append(stats.Uptime).AppendLine() + .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() + .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() + .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() + .Append("RAM Usage: ") + .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() + .Append("Audio frames (per minute): ").Append($"{stats.Frames?.Sent:#,##0} sent / {stats.Frames?.Nulled:#,##0} nulled / {stats.Frames?.Deficit:#,##0} deficit").AppendLine() + .Append("```"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString().BlockCode())); + } + + /// + /// Gets the debug log. + /// + /// The interaction context. + [SlashCommand("dbg", "Get the logs of today")] + public static async Task GetDebugLogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Log request")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); + var now = DateTime.Now; + var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; + + if (!File.Exists(targetFile)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); + + try + { + if (!File.Exists($"temp-{targetFile}")) + File.Copy(targetFile, $"temp-{targetFile}"); + else + { + File.Delete($"temp-{targetFile}"); + File.Copy(targetFile, $"temp-{targetFile}"); + } + + FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); + log.Close(); + await log.DisposeAsync(); + File.Delete($"temp-{targetFile}"); + } + catch (Exception ex) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); + if (ex.StackTrace is not null) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } + + /// + /// Monetization tests. + /// + [SlashCommandGroup("monetization", "Monetization tests")] + public class Monetization : ApplicationCommandsModule + { + private const ulong CONSUMABLE_SKU_ID = 1337743977473900555; + private const ulong DURABLE_SKU_ID = 1337744226666151949; + + /// + /// Consumes a consumable. + /// + /// The interaction context. + [SlashCommand("consume_consumable", "Consume a consumable"), ApplicationCommandRequireSkuEntitlement(CONSUMABLE_SKU_ID)] + public static async Task ConsumeConsumableAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); + + if (ctx.Entitlements.Any(x => x.Id == CONSUMABLE_SKU_ID)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable works")); + if (await ctx.Entitlements.First(x => x.Id == CONSUMABLE_SKU_ID).ConsumeAsync()) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable consumed")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable failed to consume")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); + } + + /// + /// Uses a durable. + /// + /// The interaction context. + [SlashCommand("use_durable", "Use a durable"), ApplicationCommandRequireSkuEntitlement(DURABLE_SKU_ID)] + public static async Task UseDurableAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); + + if (ctx.Entitlements.Any(x => x.Id == DURABLE_SKU_ID)) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Durable works")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); + } + } + } +} + +/// +/// Initializes a new instance of the class. +/// +/// The message. +/// The client. +/// The context menu context. +public sealed class MikuDeveloperEvalVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) +{ + /// + /// Gets or sets the message. + /// + public DiscordMessage Message { get; set; } = msg; + + /// + /// Gets or sets the channel. + /// + public DiscordChannel Channel { get; set; } = ctx.Channel; + + /// + /// Gets or sets the guild. + /// + public DiscordGuild? Guild { get; set; } = ctx.Guild; + + /// + /// Gets or sets the user. + /// + public DiscordUser User { get; set; } = ctx.User; + + /// + /// Gets or sets the member. + /// + public DiscordMember? Member { get; set; } = ctx.Member; + + /// + /// Gets or sets the context menu context. + /// + public ContextMenuContext Context { get; set; } = ctx; + + /// + /// Gets or sets the shard client. + /// + public DiscordShardedClient ShardClient { get; set; } = shard; + + /// + /// Gets or sets the client. + /// + public DiscordClient Client { get; set; } = client; +} diff --git a/MikuSharp/Commands/DiscordUtilityCommands.cs b/MikuSharp/Commands/DiscordUtilityCommands.cs new file mode 100644 index 00000000..e97c3e26 --- /dev/null +++ b/MikuSharp/Commands/DiscordUtilityCommands.cs @@ -0,0 +1,135 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("discord", "Discord Utilities")] +internal class DiscordUtilityCommands : ApplicationCommandsModule +{ + [SlashCommand("avatar", "Get the avatar of someone or yourself")] + public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null + ? user.AvatarUrl + : ctx.User.AvatarUrl).Build())); + + [SlashCommand("guild_avatar", "Get the guild avatar of someone or yourself")] + public static async Task GetGuildAvatarAsync(InteractionContext ctx, [Option("user", "User to get the guild avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null + ? ctx.GetGuildAvatarIfPossible(user) + : ctx.GetGuildAvatarIfPossible(ctx.User)).Build())); + + [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] + public static async Task GetGuildInfoAsync(InteractionContext ctx) + { + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + var members = await ctx.Guild.GetAllMembersAsync(); + var bots = members.Count(x => x.IsBot); + var emb = new DiscordEmbedBuilder(); + emb.WithTitle(ctx.Guild.Name); + if (ctx.Guild.BannerUrl is not null) + emb.WithImageUrl(ctx.Guild.BannerUrl); +#pragma warning disable DCS0200 + if (ctx.Guild.Description is not null) + emb.WithDescription(ctx.Guild.Description); +#pragma warning restore DCS0200 + emb.WithColor(new(0212255)); + if (ctx.Guild.IconUrl is not null) + emb.WithThumbnail(ctx.Guild.IconUrl); + emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "Unknown??".Italic())); + emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic())); + emb.AddField(new("ID", ctx.Guild.Id.ToString())); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime))); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})")); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString())); + emb.AddField(new("Stickers", ctx.Guild.Stickers.Count.ToString())); + emb.AddField(new("Soundboard Sounds", ctx.Guild.SoundboardSounds.Count.ToString())); + emb.AddField(new("Roles", ctx.Guild.Roles.Count.ToString())); + emb.AddField(new("Channels", ctx.Guild.Channels.Count.ToString())); + emb.AddField(new("Scheduled Events", ctx.Guild.ScheduledEvents.Count.ToString())); + emb.AddField(new("Threads", ctx.Guild.Threads.Count.ToString())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] + public static async Task GetUserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + { + var member = user is not null && ctx.Guild is not null ? ctx.Guild.TryGetMember(user.Id, out var mem) ? mem : null : ctx.Member; + user ??= ctx.User; + var emb = new DiscordEmbedBuilder(); + emb.WithColor(new(0212255)); + emb.WithTitle("User Info"); + emb.AddField(new("Username", $"{user.UsernameWithGlobalName}")); + if (member is not null) + if (member.DisplayName != (user.GlobalName ?? user.Username)) + emb.AddField(new("Nickname", $"{member.DisplayName}")); + emb.AddField(new("ID", $"{user.Id}")); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}")); + if (member is not null) + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}")); + emb.WithThumbnail(user.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("emojis", "Lists all custom emoji on this server"), DeferResponseAsync(true)] + public static async Task ListEmojisAsync(InteractionContext ctx) + { + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + if (ctx.Guild.Emojis.Count is 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("This server has no custom emojis!")); + return; + } + + var guildEmojis = ctx.Guild.Emojis.Values.ToList(); + var emojiGroups = guildEmojis.Select((emoji, index) => new + { + emoji, + index + }) + .GroupBy(x => x.index / 9) + .Select(g => g.Select(x => x.emoji).ToList()) + .ToList(); + List pages = new(emojiGroups.Count); + foreach (var group in emojiGroups) + { + DiscordEmbedBuilder builder = new(); + builder.WithTitle($"Emojis in {ctx.Guild.Name}"); + foreach (var emoji in group) + builder.AddField(new(emoji.ToString(), $"{emoji.Name} ({emoji.Id})")); + pages.Add(new(embed: builder)); + } + + await ctx.Client.GetInteractivity().SendPaginatedResponseAsync(ctx.Interaction, true, true, ctx.User, pages.Recalculate()); + } + + [SlashCommand("stickers", "Lists all custom stickers on this server"), DeferResponseAsync(true)] + public static async Task ListStickersAsync(InteractionContext ctx) + { + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + if (ctx.Guild.Stickers.Count is 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("This server has no custom stickers!")); + return; + } + + var guildStickers = ctx.Guild.Stickers.Values.ToList(); + List pages = new(guildStickers.Count); + pages.AddRange(guildStickers.Select(guildSticker => new Page(embed: new DiscordEmbedBuilder().WithTitle($"Stickers in {ctx.Guild.Name}").AddField(new("Name", guildSticker.Name)).AddField(new("ID", guildSticker.Id.ToString())).AddField(new("Description", string.IsNullOrEmpty(guildSticker.Description) ? "No description".Italic() : guildSticker.Description)).WithImageUrl(guildSticker.Url)))); + await ctx.Client.GetInteractivity().SendPaginatedResponseAsync(ctx.Interaction, true, true, ctx.User, pages.Recalculate()); + } +} diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs deleted file mode 100644 index 20e8e99c..00000000 --- a/MikuSharp/Commands/Fun.cs +++ /dev/null @@ -1,185 +0,0 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using HeyRed.Mime; - -using MikuSharp.Entities; -using MikuSharp.Utilities; - -using Newtonsoft.Json; - -using System; -using System.IO; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("fun", "Fun commands")] -internal class Fun : ApplicationCommandsModule -{ - [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var responses = new[] { "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No." }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); - } - - [SlashCommand("cat", "Get a random cat image!")] - public static async Task CatAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{ImgURL.Filetype}", ImgURL.Data); - builder.AddEmbed(ImgURL.Embed); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"clyde.png", img); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("coinflip", "Flip a coin lol")] - public static async Task CoinflipAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); - } - - [SlashCommand("dog", "Random Dog Image")] - public static async Task DogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.message})"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - /* - [SlashCommand("duck", "Random duck image")] - public static async Task DuckAsync(InteractionContext ctx) - { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); - em.WithDescription($"[Full Image]({dc.message})"); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.WithEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - }*/ - /* - [SlashCommand("lion", "Get a random lion image")] - public static async Task Lion(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/lion")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - [SlashCommand("lizard", "Get a random lizard image")] - public static async Task LizardAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(get.Url))); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } - /* - [SlashCommand("panda", "Random panda image")] - public static async Task PandaAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("penguin", "Radnom penguin image")] - public static async Task PenguinAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/penguin")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - /* - [SlashCommand("redpanda", "Random red panda image")] - public static async Task RedPandaAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/red_panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RPSAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); - } - /* - [SlashCommand("tiger", "Random tiger image")] - public static async Task TigerAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/tiger")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - /* - [SlashCommand("trumptweet", "generate a tweet by Trump")] - public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText]string text) - { - //https://nekobot.xyz/api/imagegen?type=trumptweet&text= - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=trumptweet&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"trump.png", img); - await ctx.EditResponseAsync(builder); - }*/ -} \ No newline at end of file diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs new file mode 100644 index 00000000..766db173 --- /dev/null +++ b/MikuSharp/Commands/FunCommands.cs @@ -0,0 +1,259 @@ +using MikuSharp.Attributes; +using MikuSharp.Entities; +using MikuSharp.Entities.Games; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("fun", "Fun commands", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] +internal class FunCommands : ApplicationCommandsModule +{ + [SlashCommandGroup("games", "Games")] + public class GamesCommands : ApplicationCommandsModule + { + [SlashCommand("8ball", "Yes? No? Maybe?")] + public static async Task EightBallAsync(InteractionContext ctx, [Option("question", "The question")] string question) + { + EightBallGame eightBall = new(question); + await eightBall.Send8BallMessageAsync(ctx); + } + + [SlashCommand("coinflip", "Flip a coin!")] + public static async Task CoinflipAsync(InteractionContext ctx) + { + var game = new CointossGame(ctx).TossCoin(); + await game.SendCointossMessageAsync(); + } + + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] RockPaperScissorsChoiceType userChoice) + { + var game = userChoice.ResolveRps(ctx.User); + await game.SendRpsMessageAsync(ctx); + } + } + + [SlashCommandGroup("random_images", "Random images")] + public class RandomImagesCommands : ApplicationCommandsModule + { + [SlashCommand("cat", "Get a random cat image!")] + public static async Task CatAsync(InteractionContext ctx) + { + var nekosLifeImage = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) + return; + + await ctx.SendImageMessageAsync(nekosLifeImage!.Data, "by nekos.life"); + } + + [SlashCommand("dog", "Random Dog Image")] + public static async Task DogAsync(InteractionContext ctx) + { + var dogCeo = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); + if (!await ctx.CheckForProperImageResultAsync(dogCeo)) + return; + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dogCeo.Message.ResizeLink())); + await ctx.SendImageMessageAsync(img, "by dog.ceo"); + } + + [SlashCommand("duck", "Random duck image")] + public static async Task DuckAsync(InteractionContext ctx) + { + var randomData = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); + if (!await ctx.CheckForProperImageResultAsync(randomData)) + return; + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(randomData.Message.ResizeLink())); + await ctx.SendImageMessageAsync(img, "by random-d.uk"); + } + + [SlashCommand("lizard", "Get a random lizard image")] + public static async Task LizardAsync(InteractionContext ctx) + { + var nekosLifeImage = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); + if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) + return; + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(nekosLifeImage.Url.ResizeLink())); + await ctx.SendImageMessageAsync(img, "by nekos.life"); + } + } + + [SlashCommandGroup("memes", "Meme commands (powered by nekos.life and nekobot.xyz)")] + public class MemesCommands : ApplicationCommandsModule + { + [SlashCommand("stickbug", "Get stickbugged!")] + public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("stickbug", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("trash", "Trash waifu image generator.")] + public static async Task TrashAsync(InteractionContext ctx, [Option("user", "User to trash")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("trash", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("magik", "Magikify an image!")] + public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("magik", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) }, + { "intensity", intensity.ToString() } + }); + } + + [SlashCommand("phcomment", "Make a PH comment!")] + public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) + { + await ctx.GenerateNekobotImageAsync("phcomment", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) }, + { "text", text }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } + }); + } + + [SlashCommand("blurpify", "Blurpify an image!")] + public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("blurpify", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("deepfry", "Deepfry an image!")] + public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "User to deepfry")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("deepfry", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("tweet", "Generate a fake tweet!")] + public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("tweet", new() + { + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, + { "text", text } + }); + } + + [SlashCommand("trap", "Trap someone!")] + public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User to trap")] DiscordUser user, [Option("author", "User trapping them")] DiscordUser? author = null) + { + author ??= ctx.User; + await ctx.GenerateNekobotImageAsync("trap", new() + { + { "name", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, + { "author", ctx.GetGuildOrGlobalDisplayNameIfPossible(author) }, + { "image", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("iphonex", "Insert an image into an iPhone X frame.")] + public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "User to insert")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("iphonex", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("lolice", "Call the lolice!")] + public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "User to call lolice on")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("lolice", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("kannagen", "Kanna says something!")] + public static async Task KannaGenAsync(InteractionContext ctx, [Option("text", "Text for Kanna")] string text) + { + await ctx.GenerateNekobotImageAsync("kannagen", new() + { + { "text", text } + }); + } + + [SlashCommand("changemymind", "Change my mind meme generator.")] + public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text", "Change my mind text")] string text) + { + await ctx.GenerateNekobotImageAsync("changemymind", new() + { + { "text", text } + }); + } + + [SlashCommand("whowouldwin", "Who would win?")] + public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) + { + await ctx.GenerateNekobotImageAsync("whowouldwin", new() + { + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } + }); + } + + [SlashCommand("captcha", "Generate a fake captcha!")] + public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "User to display (their name)")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("captcha", new() + { + { "url", ctx.GetGuildAvatarIfPossible(ctx.User) }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } + }); + } + + [SlashCommand("ship", "Ship two users!")] + public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) + { + await ctx.GenerateNekobotImageAsync("ship", new() + { + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } + }); + } + + [SlashCommand("baguette", "Baguette someone!")] + public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", "User to baguette")] DiscordUser? user = null) + { + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("baguette", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } + + [SlashCommand("clyde", "Say something as clyde bot")] + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await ctx.GenerateNekobotImageAsync("clyde", new() + { + { "text", text } + }); + } + } +} diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuildCommands.cs similarity index 66% rename from MikuSharp/Commands/MikuGuild.cs rename to MikuSharp/Commands/MikuGuildCommands.cs index f44d4875..949500f3 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuildCommands.cs @@ -1,28 +1,21 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; -public class MikuGuild : ApplicationCommandsModule +public class MikuGuildCommands : ApplicationCommandsModule { [SlashCommand("smolcar", "#SmolArmy")] public static async Task SmolCarAsync(InteractionContext ctx) { + if (ctx.Guild is null || ctx.Member is null) + return; + if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) { - await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)!); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); } else { - await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)!); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); } } diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/ModerationCommands.cs similarity index 67% rename from MikuSharp/Commands/Moderation.cs rename to MikuSharp/Commands/ModerationCommands.cs index c0b63a74..7a94bbc7 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/ModerationCommands.cs @@ -1,96 +1,88 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; +using DisCatSharp.Exceptions; +using MikuSharp.Attributes; using MikuSharp.Utilities; -using System; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; -[SlashCommandGroup("mod", "Moderation", defaultMemberPermissions: (long)Permissions.BanMembers, dmPermission: false)] -internal class Moderation : ApplicationCommandsModule +[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true)] +internal class ModerationCommands : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); try { await ctx.Guild.DisableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Disabled invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not disable invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); } } [SlashCommand("enable_invites", "Enable invites usage for guild")] public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); try { await ctx.Guild.EnableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Enabled invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not enable invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); } } [SlashCommand("ban", "Ban someone")] - public static async Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task BanAsync( + InteractionContext ctx, + [Option("user", "User to ban")] DiscordUser user, + [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] + int deletionDays = 0, + [Option("reason", "Auditlog reason")] string? reason = null + ) { - await ctx.DeferAsync(false); try { await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); } } [SlashCommand("unban", "Unban someone")] public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); var userId = Convert.ToUInt64(id); var user = await ctx.Client.GetUserAsync(userId, true); await ctx.Guild.UnbanMemberAsync(user, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); } [SlashCommand("kick", "Kick someone")] public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); try { var member = await user.ConvertToMember(ctx.Guild); await member.RemoveAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); } } [SlashCommand("purge", "Delete a large amount of messages fast")] public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(true); try { var msgs = await ctx.Channel.GetMessagesAsync(amount); @@ -99,9 +91,9 @@ public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "A await ctx.Channel.DeleteMessagesAsync(under14DaysOld, reason); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Purged {under14DaysOld.Count} messages")); } - catch (DisCatSharp.Exceptions.BadRequestException ex) + catch (BadRequestException ex) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(Formatter.BlockCode(ex.JsonMessage, "json"))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); } } } diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs deleted file mode 100644 index 71eeb8c2..00000000 --- a/MikuSharp/Commands/Music.cs +++ /dev/null @@ -1,695 +0,0 @@ -/*using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Attributes; -using MikuSharp.Entities; -using MikuSharp.Enums; -using MikuSharp.Events; -using MikuSharp.Utilities; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -/// -/// The music commands -/// -[SlashCommandGroup("music", "Music commands", dmPermission: false)] -public class Music : ApplicationCommandsModule -{ - private static readonly string[] Units = new[] { "", "ki", "Mi", "Gi" }; - private static string SizeToString(long l) - { - double d = l; - var u = 0; - while (d >= 900 && u < Units.Length - 2) - { - u++; - d /= 1024; - } - - return $"{d:#,##0.00} {Units[u]}B"; - } - - [SlashCommandGroup("base", "Base commands")] - public class Base : ApplicationCommandsModule - { - [SlashCommand("join", "Joins the voice channel you're in")] - [RequireUserVoicechatConnection] - public static async Task JoinAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.musicInstance.usedChannel = ctx.Channel; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - } - - [SlashCommand("leave", "Leaves the channel")] - public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (g.musicInstance == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not in a voice channel")); - return; - } - g.musicInstance.playstate = Playstate.NotPlaying; - try - { - if (keep) - await g.musicInstance.guildConnection.StopAsync(); - await g.musicInstance.guildConnection.DisconnectAsync(); - if (!keep) - await Database.ClearQueue(ctx.Guild); - g.musicInstance = null; - } - catch (Exception) - { } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } - - - [SlashCommand("lstats", "Displays Lavalink statistics")] - [ApplicationCommandRequireOwner] - public static async Task GetLavalinkStatsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var stats = MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Statistics; - var sb = new StringBuilder(); - sb.Append("Lavalink resources usage statistics: ```") - .Append("Uptime: ").Append(stats.Uptime).AppendLine() - .Append("Players: ").AppendFormat("{0} active / {1} total", stats.ActivePlayers, stats.TotalPlayers).AppendLine() - .Append("CPU Cores: ").Append(stats.CpuCoreCount).AppendLine() - .Append("CPU Usage: ").AppendFormat("{0:#,##0.0%} lavalink / {1:#,##0.0%} system", stats.CpuLavalinkLoad, stats.CpuSystemLoad).AppendLine() - .Append("RAM Usage: ").AppendFormat("{0} allocated / {1} used / {2} free / {3} reservable", SizeToString(stats.RamAllocated), SizeToString(stats.RamUsed), SizeToString(stats.RamFree), SizeToString(stats.RamReservable)).AppendLine() - .Append("Audio frames (per minute): ").AppendFormat("{0:#,##0} sent / {1:#,##0} nulled / {2:#,##0} deficit", stats.AverageSentFramesPerMinute, stats.AverageNulledFramesPerMinute, stats.AverageDeficitFramesPerMinute).AppendLine() - .Append("```"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); - } - } - - [SlashCommandGroup("playback", "Playback controls")] - public class Playback : ApplicationCommandsModule - { - [SlashCommand("seek", "Seek a song")] - [RequireUserAndBotVoicechatConnection] - public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) - { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - if (g.musicInstance.playstate != Playstate.Playing && g.musicInstance.playstate != Playstate.Paused) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); - return; - } - g.musicInstance.usedChannel = ctx.Channel; - var ts = TimeSpan.FromSeconds(position); - await g.musicInstance.guildConnection.SeekAsync(ts); - var pos = ts.Hours < 1 ? ts.ToString(@"mm\:ss") : ts.ToString(@"hh\:mm\:ss"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.musicInstance.currentSong.track.Title} to {pos}")); - } - - [SlashCommand("play", "Play or queue a song")] - [RequireUserVoicechatConnection] - public static async Task PlayAsync(InteractionContext ctx, - [Option("song", "Song name or url to play")] string name_or_url = null, - [Option("music_file", "Music file to play")] DiscordAttachment music_file = null - ) - { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - var curq = await Database.GetQueueAsync(ctx.Guild); - if (curq.Count != 0 && g.musicInstance.playstate == Playstate.NotPlaying) - { - var inter = ctx.Client.GetInteractivity(); - List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "restore", "Restore old queue"), - new DiscordButtonComponent(ButtonStyle.Danger, "clear", "Clear old queue") - }; - var msg = await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Recover Queue").WithDescription("The last time the bot disconnected the queue wasnt cleared, do you want to restore and play that old one?").Build()).AddComponents(buttons)); - var hmm = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (hmm.TimedOut) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Timed out!")); - return; - } - else if (hmm.Result.Id == "restore") - { - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Restored").AddComponents(buttons)); - await g.musicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - await g.musicInstance.PlaySong(); - return; - } - else - { - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - await Database.ClearQueue(ctx.Guild); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cleared").AddComponents(buttons)); - } - } - - await g.ConditionalConnect(ctx); - - if (music_file == null && name_or_url == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - g.musicInstance.usedChannel = ctx.Channel; - name_or_url = music_file.SearchUrlOrAttachment(name_or_url); - var oldState = g.musicInstance.playstate; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {name_or_url}...")); - var q = await g.musicInstance.QueueSong(name_or_url, ctx); - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - var emb = new DiscordEmbedBuilder(); - if (oldState == Playstate.Playing) - { - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\n" + - $"Requested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Added").Build()).AsEphemeral()); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new DiscordEmbedField(q.Tracks[q.PlaylistInfo.SelectedTrack].Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Author}\nRequested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); - } - } - - [SlashCommand("insert", "Queue a song at a specific position!")] - [RequireUserVoicechatConnection] - public static async Task InsertToQueueAsync(InteractionContext ctx, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string posi, - [Option("song", "Song name or url to play")] string name_or_url = null, - [Option("music_file", "Music file to play")] DiscordAttachment music_file = null - ) - { - await ctx.DeferAsync(true); - var pos = Convert.ToInt32(posi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (pos < 1) - return; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - - await g.ConditionalConnect(ctx); - - if (music_file == null && name_or_url == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - g.musicInstance.usedChannel = ctx.Channel; - name_or_url = music_file.SearchUrlOrAttachment(name_or_url); - var oldState = g.musicInstance.playstate; - var q = await g.musicInstance.QueueSong(name_or_url, ctx, pos); - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - var emb = new DiscordEmbedBuilder(); - if (oldState == Playstate.Playing) - { - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\n" + - $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Playing"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new DiscordEmbedField(q.Tracks[q.PlaylistInfo.SelectedTrack].Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Added"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - } - - [SlashCommand("skip", "Skip the current song")] - [RequireUserAndBotVoicechatConnection] - public static async Task SkipSongAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.guildConnection.PlaybackFinished -= Lavalink.LavalinkTrackFinish; - if (g.musicInstance.currentSong != null) - { - if (g.musicInstance.repeatMode != RepeatMode.On && g.musicInstance.repeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, ctx.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.musicInstance.currentSong.track.TrackString); - else if (lastPlayedSongs[0]?.track.Uri != g.musicInstance.currentSong.track.Uri) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - queue = await Database.GetQueueAsync(ctx.Guild); - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - if (queue.Count != 0) - await g.musicInstance.PlaySong(); - else - { - g.musicInstance.playstate = Playstate.NotPlaying; - await g.musicInstance.guildConnection.StopAsync(); - } - if (g.musicInstance.lastSong != null) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.musicInstance.lastSong.track.Title}").Build())); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Continued!**").Build())); - } - - [SlashCommand("stop", "Stop Playback")] - [RequireUserAndBotVoicechatConnection] - public static async Task StopAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - await Task.Run(async () => await g.musicInstance.guildConnection.StopAsync()); - var cmd_id = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; - await ctx.EditResponseAsync(builder: new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); - } - - [SlashCommand("volume", "Change the music volume")] - [RequireUserAndBotVoicechatConnection] - public static async Task ModifyVolumeAsync(InteractionContext ctx, - [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] int vol = 100 - ) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - if (vol > 150) vol = 150; - await g.musicInstance.guildConnection.SetVolumeAsync(vol); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Set volume to {vol}**").Build())); - } - - [SlashCommand("pause", "Pauses playback")] - [RequireUserAndBotVoicechatConnection] - public static async Task PauseAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - if (g.musicInstance.playstate == Playstate.Playing) - { - await g.musicInstance.guildConnection.PauseAsync(); - g.musicInstance.playstate = Playstate.Paused; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Paused**").Build())); - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not playing anything right now")); - } - - [SlashCommand("resume", "Resumes paused playback")] - [RequireUserAndBotVoicechatConnection] - public static async Task ResumeAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - if (g.musicInstance.playstate == Playstate.Stopped) - { - await g.musicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Started Playback**").Build())); - } - else - { - await g.musicInstance.guildConnection.ResumeAsync(); - g.musicInstance.playstate = Playstate.Playing; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Resumed**").Build())); - } - } - } - - [SlashCommandGroup("queue", "Queue management")] - [RequireUserAndBotVoicechatConnection] - public class Queue : ApplicationCommandsModule - { - [SlashCommand("show", "Show the current queue")] - public static async Task ShowQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var queue = await Database.GetQueueAsync(ctx.Guild); - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = queue.Count / 5; - if ((queue.Count % 5) != 0) - totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - if (g.musicInstance.repeatMode == RepeatMode.All) - { - songAmount = g.musicInstance.repeatAllPos; - foreach (var Track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{g.musicInstance.currentSong.track.Title.Replace("*", "").Replace("|", "")}** by {g.musicInstance.currentSong.track.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.musicInstance.currentSong.addedBy}> [Link]({g.musicInstance.currentSong.track.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - queue.ElementAt(songAmount).GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{queue.ElementAt(songAmount).track.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{queue.ElementAt(songAmount).addedBy}> [Link]({queue.ElementAt(songAmount).track.Uri.AbsoluteUri})")); - } - songsPerPage++; - songAmount++; - if (songAmount == queue.Count) - songAmount = 0; - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - if (songAmount == g.musicInstance.repeatAllPos) - { - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - } - else - { - foreach (var Track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new DiscordEmbedField($"**{g.musicInstance.currentSong.track.Title.Replace("*", "").Replace("|", "")}** by {g.musicInstance.currentSong.track.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.musicInstance.currentSong.addedBy}> [Link]({g.musicInstance.currentSong.track.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - Track.GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{Track.addedBy}> [Link]({Track.track.Uri.AbsoluteUri})")); - } - songsPerPage++; - songAmount++; - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - } - if (currentPage == 1) - { - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => !x.Embed.Fields.Any(y => y.Name != "Playback keep")).ToList()) - Pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - - [SlashCommand("clear", "Clears the queue")] - public static async Task ClearQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - await Database.ClearQueue(ctx.Guild); - if (g.musicInstance.currentSong != null) - await Database.AddToQueue(ctx.Guild, g.musicInstance.currentSong.addedBy, g.musicInstance.currentSong.track.TrackString); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Cleared queue!**").Build())); - } - - [SlashCommand("move", "Moves a specific song within the queue")] - public static async Task MoveWithinQueueAsync(InteractionContext ctx, - [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string old_posi, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string new_posi - ) - { - await ctx.DeferAsync(true); - var old_pos = Convert.ToInt32(old_posi); - var new_pos = Convert.ToInt32(new_posi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - if (old_pos < 1 || new_pos < 1 || old_pos == new_pos || new_pos >= queue.Count) - return; - var oldSong = queue[old_pos]; - await Database.MoveQueueItems(ctx.Guild, old_pos, new_pos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Moved**:\n **{oldSong.track.Title}**\nby {oldSong.track.Author}\n from position **{old_pos}** to **{new_pos}**!").Build())); - } - - [SlashCommand("remove", "Removes a name_or_url from queue")] - public static async Task RemoveFromQueueAsync(InteractionContext ctx, - [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string posi - ) - { - var position = Convert.ToInt32(posi); - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - var old = queue[position]; - await Database.RemoveFromQueueAsync(position, ctx.Guild); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.track.Title}**\nby {old.track.Author}").Build())); - } - } - - [SlashCommandGroup("options", "Playback Options")] - [RequireUserAndBotVoicechatConnection] - public class PlaybackOptions : ApplicationCommandsModule - { - [SlashCommand("repeat", "Repeat the current song or the entire queue")] - public static async Task RepeatAsync(InteractionContext ctx, - [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] RepeatMode mode) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.repeatMode = mode; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.musicInstance.repeatMode}**").Build())); - } - - [SlashCommand("shuffle", "Play the queue in shuffle mode")] - public static async Task ShuffleAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.shuffleMode = g.musicInstance.shuffleMode == ShuffleMode.Off ? ShuffleMode.On : ShuffleMode.Off; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.musicInstance.shuffleMode}**").Build())); - } - } - - [SlashCommandGroup("info", "Playing info")] - public class PlayingInfo : ApplicationCommandsModule - { - [SlashCommand("now_playing", "Show whats currently playing")] - public static async Task ShowNowPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.shardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Now Playing"); - eb.WithDescription("**__Current Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g, null); - } - - [SlashCommand("last_playing", "Show what played before")] - public static async Task ShowLastPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.shardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Last playing"); - eb.WithDescription("**__Previous Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g, lastPlayedSongs); - } - - [SlashCommand("last_playing_list", "Show what songs were played before")] - public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (lastPlayedSongs.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = lastPlayedSongs.Count / 10; - if ((lastPlayedSongs.Count % 10) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in lastPlayedSongs) - { - Track.GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}", $"by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({Track.track.Uri})")); - songsPerPage++; - songAmount++; - if (songsPerPage == 10) - { - songsPerPage = 0; - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - if (songAmount == lastPlayedSongs.Count) - { - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - Pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs new file mode 100644 index 00000000..81db5735 --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs @@ -0,0 +1,47 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The info commands. + /// + [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class InfoCommands : ApplicationCommandsModule + { + /// + /// Shows the currently playing song. + /// + /// The interaction context. + [SlashCommand("now_playing", "Show whats currently playing")] + public static async Task ShowNowPlaylingAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Now playing")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the last played song. + /// + /// The interaction context. + [SlashCommand("last_played", "Show what played before")] + public static async Task ShowLastPlayedAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Last played")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the last playing songs. + /// + /// The interaction context. + [SlashCommand("last_playing", "Show what songs were played before")] + public static async Task ShowLastPlayingAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Last playing list")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs new file mode 100644 index 00000000..da8665e9 --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -0,0 +1,68 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The options commands. + /// + [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class OptionsCommands : ApplicationCommandsModule + { + /// + /// Changes the repeat mode. + /// + /// The interaction context. + /// The new repeat mode. + [SlashCommand("repeat", "Repeat the current song or the entire queue")] + public static async Task RepeatAsync( + InteractionContext ctx, + [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] + RepeatMode mode + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdateRepeatMode(mode); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + }); + } + + /// + /// Shuffles the queue. + /// + /// The interaction context. + [SlashCommand("shuffle", "Shuffle the queue")] + public static async Task ShuffleAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + }); + } + + /// + /// Changes the volume of the music player. + /// + /// The interaction context. + /// The volume to set. + [SlashCommand("volume", "Change the music volume")] + public static async Task ModifyVolumeAsync( + InteractionContext ctx, + [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] + int volume = 100 + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); + }); + } + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs new file mode 100644 index 00000000..906c1df3 --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -0,0 +1,94 @@ +using MikuSharp.Attributes; +using MikuSharp.Enums; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The playback commands. + /// + [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class PlaybackCommands : ApplicationCommandsModule + { + /// + /// Pauses the playback. + /// + /// The interaction context. + [SlashCommand("pause", "Pauses the playback"), RequirePlaybackState(PlaybackState.Playing)] + public async Task PauseAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + await musicSession.LavalinkGuildPlayer.PauseAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Paused); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Paused the playback! "); + }); + } + + /// + /// Resumes the playback. + /// + /// The interaction context. + [SlashCommand("resume", "Resumes the playback"), RequirePlaybackState(PlaybackState.Paused)] + public async Task ResumeAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Resumed the playback!"); + }); + } + + /// + /// Stops the playback. + /// + /// The interaction context. + [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] + public static async Task StopAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdateRepeatMode(RepeatMode.None); + musicSession.LavalinkGuildPlayer.ClearQueue(); + await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); + await ctx.EditResponseAsync("Stopped the playback!"); + }); + } + + /// + /// Seeks the currently playing song to given position. + /// + /// The interaction context. + /// The seconds to seek to. + [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] + public static async Task SeekAsync(InteractionContext ctx, [Option("seconds", "Seconds to seek to")] int seconds) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + var targetSeek = TimeSpan.FromSeconds(seconds); + await musicSession.LavalinkGuildPlayer.SeekAsync(seconds); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); + }); + } + + /// + /// Plays a url. + /// + /// The interaction context. + /// The url to play. + /// Whether to shuffle playlists. + [SlashCommand("play", "Plays a url")] + public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url, [Option("shuffle_playlist", "Shuffle playlists")] bool shufflePlaylists = false) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await musicSession.LoadAndPlayTrackAsync(ctx, url, shufflePlaylists)); + } + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs new file mode 100644 index 00000000..8b331a4d --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -0,0 +1,105 @@ +using MikuSharp.Attributes; +using MikuSharp.Enums; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The queue commands. + /// + [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class QueueCommands : ApplicationCommandsModule + { + /// + /// Shows the current queue. + /// + /// The interaction context. + [SlashCommand("show", "Show the current queue")] + public static async Task ShowQueueAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue list")); }); + } + + /// + /// Skips to the next song. + /// + /// The interaction context. + [SlashCommand("skip", "Skips to the next song")] + public static async Task SkipAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => + { + if (musicSession.LavalinkGuildPlayer!.TryPeekQueue(out _)) + { + await musicSession.LavalinkGuildPlayer.SkipAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + }); + } + + /// + /// Skips to the given queue position. + /// + /// The interaction context. + /// The position in the queue. + [SlashCommand("skip_to", "Skips to given queue position")] + public static async Task SkipToAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("position", "Position in queue", true)] int position) + { + if (position is -1) + { + await ctx.EditResponseAsync("Something went wrong while parsing the queue position."); + return; + } + + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.LavalinkGuildPlayer!.RemoveFromQueueAtRange(0, position); + await musicSession.LavalinkGuildPlayer.SkipAsync(); + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out var nextTrack)) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Successfully skipped to {nextTrack.Info.Title.Bold()}!")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + }); + } + + /// + /// Removes a song from the queue. + /// + /// The interaction context. + /// The position in the queue. + [SlashCommand("remove", "Remvoes a song from the queue")] + public static async Task RemoveAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("song", "Song to remove", true)] int position) + { + if (position is -1) + { + await ctx.EditResponseAsync("Something went wrong while parsing the song."); + return; + } + + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.LavalinkGuildPlayer!.RemoveFromQueueAt(position); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully removed the song from the queue!")); + }); + } + + /// + /// Clears the queue. + /// + /// The interaction context. + [SlashCommand("clear", "Clear Queue"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] + public static async Task StopAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdateRepeatMode(RepeatMode.None); + musicSession.LavalinkGuildPlayer.ClearQueue(); + await ctx.EditResponseAsync("Cleared the queue!"); + }); + } + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs new file mode 100644 index 00000000..cd5f6328 --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -0,0 +1,138 @@ +using DisCatSharp.Exceptions; + +using MikuSharp.Attributes; +using MikuSharp.Entities; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +/// +/// The music commands +/// +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), EnsureLavalinkSession] +public partial class MusicCommands : ApplicationCommandsModule +{ + /// + /// Joins a voice channel the user is in. + /// + /// The interaction context. + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession, DeferResponseAsync(true)] + public async Task JoinAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); + ArgumentNullException.ThrowIfNull(ctx.Guild); + await ctx.ExecuteWithMusicSessionAsync(async (_, _) => await ctx.EditResponseAsync("I'm already connected"), + async guildId => + { + var session = ctx.Client.GetLavalink().DefaultSession(); + ArgumentNullException.ThrowIfNull(session); + await session.ConnectAsync(ctx.Member.VoiceState.Channel); + var musicSession = await new MusicSession(ctx.Member.VoiceState.Channel, ctx.Guild, session).InjectPlayerAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); + HatsuneMikuBot.MusicSessions[guildId] = musicSession; + }); + } + + /// + /// Leaves a voice channel. + /// + /// The interaction context. + [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public async Task LeaveAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + if (musicSession.LavalinkGuildPlayer is not null) + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); + if (musicSession.StatusMessage is not null) + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")), + guildId => Task.FromResult(HatsuneMikuBot.MusicSessionLocks.TryRemove(guildId, out _))); + } + + [SlashCommand("test", "Test UI Kit"), ApplicationCommandRequireTeamMember, DeferResponseAsync] + public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The identifier (lavalink identifier, url, etc..)")] string identifier) + { + DiscordWebhookBuilder builder = new(); + LavalinkTrack track; + var session = ctx.Client.GetLavalink().DefaultSession()!; + var loadResult = await session.LoadTracksAsync(identifier); + switch (loadResult.LoadType) + { + case LavalinkLoadResultType.Track: + track = loadResult.GetResultAs(); + break; + case LavalinkLoadResultType.Search: + var tracks = loadResult.GetResultAs>(); + track = tracks.First(); + break; + case LavalinkLoadResultType.Playlist: + case LavalinkLoadResultType.Empty: + case LavalinkLoadResultType.Error: + default: + throw new ArgumentOutOfRangeException(null, "Unsupported result type"); + } + + var lyrics = await session.GetLyricsAsync(track); + + var artistArtworkUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistArtworkUrl", out var url1) ? url1.ToString()! : null; + var albumName = track.PluginInfo.AdditionalProperties.TryGetValue("albumName", out var url2) ? url2.ToString() : null; + var albumUrl = track.PluginInfo.AdditionalProperties.TryGetValue("albumUrl", out var url3) ? url3.ToString() : null; + var artistUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistUrl", out var url4) ? url4.ToString() : null; + + DiscordSeparatorComponent separator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent trackInfo = new($"### {track.Info.Title}\n**Artist:** {track.Info.Author.InlineCode()}\n**Length:** {track.Info.Length.FormatTimeSpan().InlineCode()}{(!string.IsNullOrEmpty(albumName) ? $"\n**Album:** {albumName.InlineCode()}" : "")}\n**Stream:** {track.Info.IsStream.ToString().ToLowerInvariant().InlineCode()}"); + DiscordSectionComponent trackInfoSection = new([trackInfo]); + trackInfoSection.WithThumbnailComponent(artistArtworkUrl!, "Artist Artwork"); + DiscordTextDisplayComponent additionalTrackInfo = new($"-# **ISrc:** {track.Info.Isrc?.InlineCode() ?? "none".InlineCode()} **Identifier:** {track.Info.Identifier.InlineCode()}"); + DiscordMediaGalleryItem trackCover = new(track.Info.ArtworkUrl!.AbsoluteUri, "Song Artwork"); + DiscordMediaGalleryComponent trackMediaGallery = new([trackCover]); + DiscordLinkButtonComponent trackLink = new(track.Info.Uri.AbsoluteUri, "View", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); + DiscordActionRowComponent links1 = new([trackLink]); + List infoComponents = [string.IsNullOrEmpty(artistArtworkUrl) ? trackInfo : trackInfoSection, separator, trackMediaGallery, separator, links1]; + if (!string.IsNullOrEmpty(albumUrl)) + { + DiscordLinkButtonComponent albumLink = new(albumUrl, "View Album", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); + DiscordActionRowComponent links2 = new([albumLink]); + infoComponents.Add(links2); + } + + if (!string.IsNullOrEmpty(artistUrl)) + { + DiscordLinkButtonComponent artistLink = new(artistUrl, "View Artist", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); + DiscordActionRowComponent links3 = new([artistLink]); + infoComponents.Add(links3); + } + + infoComponents.AddRange([separator, additionalTrackInfo]); + + DiscordContainerComponent infoContainer = new([..infoComponents]); + MemoryStream? ms = null; + try + { + await ctx.EditResponseAsync(builder.WithV2Components().AddComponents(infoContainer)); + + if (lyrics is not null && lyrics.Lines.Count > 0) + { + var lyricString = string.Join("\n", lyrics.Lines.Select(line => string.IsNullOrEmpty(line.Line) ? string.Empty : $"{TimeSpan.FromMilliseconds(line.Timestamp).FormatTimeSpan()}{(line.Duration.HasValue ? $" {TimeSpan.FromMilliseconds(line.Duration.Value).FormatTimeSpan()}" : string.Empty)}: {line.Line}")); + ms = new(Encoding.UTF8.GetBytes(lyricString)); + ms.Position = 0; + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Lyrics for {lyrics.SourceName}{(!string.IsNullOrEmpty(lyrics.Provider) ? $" by {lyrics.Provider}" : string.Empty)}").AddFile("SPOILER_lyrics.txt", ms, description: $"Lyrics for {track.Info.Title}")); + } + } + catch (BadRequestException ex) + { + await ctx.EditResponseAsync(builder.WithContent(ex.Errors?.BlockCode("json") ?? ex.Message)); + } + finally + { + if (ms is not null) + await ms.DisposeAsync(); + } + } +} diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs deleted file mode 100644 index 7c47e271..00000000 --- a/MikuSharp/Commands/NSFW.cs +++ /dev/null @@ -1,122 +0,0 @@ -using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; -using DisCatSharp.Entities; - -using MikuSharp.Utilities; - -using System.Threading.Tasks; - -namespace MikuSharp.Commands -{ - [RequireNsfw] - class NSFW : BaseCommandModule - { - [Command("4k")] - [Description("lewd")] - public async Task FourK(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("anal")] - [Description("lewd")] - public async Task Anal(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("ass")] - [Description("lewd")] - public async Task Ass(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("gonewild")] - [Description("lewd")] - public async Task Gonewild(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdkitsune")] - [Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdneko")] - [Description("lewd")] - public async Task LewdNeko(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - [Command("porngif")] - [Description("lewd")] - public async Task PornGif(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("pussy")] - [Description("lewd")] - public async Task Pussy(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("thighs")] - [Aliases("thigh")] - [Description("lewd")] - public async Task Thighs(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - } -} diff --git a/MikuSharp/Commands/NsfwCommands.cs b/MikuSharp/Commands/NsfwCommands.cs new file mode 100644 index 00000000..9cb0d413 --- /dev/null +++ b/MikuSharp/Commands/NsfwCommands.cs @@ -0,0 +1,161 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[RequireNsfw, NotDiscordStaff] +public class NsfwCommands : BaseCommandModule +{ + [Command("4k"), Description("lewd")] + public async Task FourK(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("anal"), Description("lewd")] + public async Task Anal(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("ass"), Description("lewd")] + public async Task Ass(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("gonewild"), Description("lewd")] + public async Task Gonewild(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdkitsune"), Description("lewd")] + public async Task LewdKitsune(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdneko"), Description("lewd")] + public async Task LewdNeko(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("porngif"), Description("lewd")] + public async Task PornGif(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("pussy"), Description("lewd")] + public async Task Pussy(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("thighs"), Aliases("thigh"), Description("lewd")] + public async Task Thighs(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } +} diff --git a/MikuSharp/Commands/Playlist.cs b/MikuSharp/Commands/Playlist.cs deleted file mode 100644 index 591b2f8d..00000000 --- a/MikuSharp/Commands/Playlist.cs +++ /dev/null @@ -1,495 +0,0 @@ -/*using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Attributes; -using MikuSharp.Entities; -using MikuSharp.Enums; -using MikuSharp.Utilities; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("playlists", "Manage your playlists", dmPermission: false)] -public class Playlists : ApplicationCommandsModule -{ - [SlashCommandGroup("new", "Playlist creation")] - public class PlaylistCreation : ApplicationCommandsModule - { - [SlashCommand("copy_queue", "Copy the current queue to a playlist!")] - [RequireUserAndBotVoicechatConnection] - public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var q = await Database.GetQueueAsync(ctx.Guild); - if (q.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Nothing in queue")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Copy Queue").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - foreach (var e in q) - { - await PlaylistDB.AddEntry(name, ctx.Member.Id, e.track.TrackString); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Queue Copy").WithDescription("Queue was saved to new playlist -> " + name).Build())); - } - - [SlashCommand("create", "Create a playlist")] - public static async Task CreatePlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("New Playlist was created -> " + name).Build())); - } - - [SlashCommand("create_fixed", "Create a fixed playlist (linked to a Youtube or Soundcloud playlist)")] - public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name, - [Option("link", "Link to playlist")] string link - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - LavalinkLoadResult s = null; - try - { - s = await MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Rest.GetTracksAsync(new Uri(link)); - } - catch - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (s.LoadResultType != LavalinkLoadResultType.PlaylistLoaded) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (link.Contains("youtu") && !link.Contains("soundcloud")) - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Youtube, link); - } - else - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Soundcloud, link); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription($"Fixed playlist created with playlist -> {name} and {s.Tracks.Count} Songs!").Build())); - } - } - - [SlashCommandGroup("manage", "Playlist management")] - public class PlaylistManagement : ApplicationCommandsModule - { - [SlashCommand("list", "List all your playlists")] - public static async Task ListPlaylistsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (pls.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You dont have any playlists")); - return; - } - //ctx.Client.Logger.LogDebug(pls.Count.ToString()); - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = pls.Count / 5; - if ((pls.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in pls) - { - //ctx.Client.Logger.LogDebug(Track.Value == null); - //ctx.Client.Logger.LogDebug(Track.Key); - int songam = 0; - var ent = await Track.Value.GetEntries(); - songam = ent.Count; - string sub = ""; - if (Track.Value.ExternalService == ExtService.None) - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"Last modified on: {Track.Value.Modify}"; - } - else - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"{Track.Value.ExternalService} [Link]({Track.Value.Url})"; - } - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.Key}** ({songam} Songs)", sub)); - songsPerPage++; - songAmount++; - emb.WithTitle($"List Playlists"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == pls.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - - [SlashCommand("show", "Show the contents of a playlist")] - public static async Task ShowPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to show", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Show Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var q = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var queue = await q.GetEntries(); - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist empty!")); - return; - } - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = queue.Count / 5; - if ((queue.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in queue) - { - string time = ""; - if (Track.track.Length.Hours < 1) time = Track.track.Length.ToString(@"mm\:ss"); - else time = Track.track.Length.ToString(@"hh\:mm\:ss"); - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Added on {Track.additionDate} [Link]({Track.track.Uri.AbsoluteUri})")); - songsPerPage++; - songAmount++; - emb.WithTitle($"Songs in {playlist}"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - - [SlashCommand("delete", "Delete a playlist")] - public static async Task DeletePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to delete", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.RemovePlaylist(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("Deleted playlist -> " + playlist).Build())); - } - - [SlashCommand("rename", "Rename a playlist")] - public static async Task RenamePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("name", "New name for playlist")] string name - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription("**Error** You dont have a playlist with that name!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - await pls.GetEntries(); - - await PlaylistDB.RenameList(playlist, ctx.Member.Id, name); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription($"Renamed Playlist to {playlist} -> {name}!").Build())); - } - - [SlashCommand("clear", "Clear all entries from a playlist")] - public static async Task ClearPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to clear", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.ClearList(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription($"Cleared all songs from playlist -> {playlist}!").Build())); - } - - [SlashCommand("play", "Play a playlist/Add the songs to the queue")] - [RequireUserVoicechatConnection] - public static async Task PlayPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to play", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - var ps = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (!ps.Any(x => x.Key == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var p = await pls.GetEntries(); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - { - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - } - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.musicInstance.usedChannel = ctx.Channel; - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p); - if (g.musicInstance.guildConnection.IsConnected && (g.musicInstance.playstate == Playstate.NotPlaying || g.musicInstance.playstate == Playstate.Stopped)) - await g.musicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription($"Playing playlist/Added to queue!").Build())); - } - } - - [SlashCommandGroup("song", "Song management")] - public class SongOperations : ApplicationCommandsModule - { - [SlashCommand("add", "Add a song to a playlist")] - public static async Task AddSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - await PlaylistDB.AddEntry(playlist, ctx.Member.Id, got.Tracks); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription($"Added entry -> {got.Tracks[0].Title}!").Build())); - } - - [SlashCommand("insert_at", "Insert a song into a playlist at a choosen position")] - public static async Task InsertAtAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("position", "Position to insert song at" ,true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - got.Tracks.Reverse(); - await PlaylistDB.InsertEntry(ctx.Guild, playlist, ctx.Member.Id, got.Tracks, pos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription($"Inserted entry -> {got.Tracks[0].Title} at {pos}!").Build())); - } - - [SlashCommand("move", "Move a song to a specific position in your playlist")] - public static async Task MoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, - [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string newposi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var oldpos = Convert.ToInt32(oldposi); - var newpos = Convert.ToInt32(newposi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant move songs!").Build())); - return; - } - var e = await pls.GetEntries(); - if (e[newpos] == null | e[oldpos] == null) - return; - await PlaylistDB.MoveListItems(ctx.Guild, playlist, ctx.Member.Id, oldpos, newpos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Move Song").WithDescription($"Moved entry -> {e[oldpos].track.Title} to position {newpos}!").Build())); - } - - [SlashCommand("remove", "Remove a song from a playlist")] - public static async Task RemoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var ents = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var en = await ents.GetEntries(); - await PlaylistDB.RemoveFromList(ctx.Guild, pos, playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription($"Entry removed! -> {en[pos].track.Title}").Build())); - } - } - - public static bool CheckError(string playlist) - { - if (playlist == "error") - return true; - else - return false; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs new file mode 100644 index 00000000..0ad47bfe --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs @@ -0,0 +1,57 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Playlist; + +public partial class PlaylistCommands +{ + /// + /// Provides commands for creating playlists. + /// + [SlashCommandGroup("create", "Playlist creation"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class CreateCommands : ApplicationCommandsModule + { + /// + /// Copies the current queue to a new playlist. + /// + /// The interaction context. + /// The name of the new playlist. + [SlashCommand("from_queue", "Copy the current queue to a playlist!")] + public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from queue")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + + /// + /// Creates a new blank playlist. + /// + /// The interaction context. + /// The name of the new playlist. + [SlashCommand("blank", "Create a new playlist from scratch")] + public static async Task CreateBlankPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from scratch")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + + /// + /// Creates a new playlist from another playlist (e.g., YouTube, Soundcloud, or Spotify). + /// + /// The interaction context. + /// The name of the new playlist. + /// The link to the source playlist. + [SlashCommand("create", "Create a new playlist from another playlist (Like YouTube, Soundcloud or Spotify)")] + public static async Task CreatePlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name, [Option("source", "The playlist to use")] string link) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from playlist")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs new file mode 100644 index 00000000..9a08061e --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs @@ -0,0 +1,86 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Playlist; + +public partial class PlaylistCommands +{ + /// + /// Provides commands for managing playlists. + /// + [SlashCommandGroup("manage", "Playlist management"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class ManageCommands : ApplicationCommandsModule + { + /// + /// Lists all your playlists. + /// + /// The interaction context. + [SlashCommand("list", "List all your playlists")] + public static async Task ListPlaylistsAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlists")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the contents of a playlist. + /// + /// The interaction context. + /// The name of the playlist to show. + [SlashCommand("show", "Show the contents of a playlist")] + public static async Task ShowPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to show", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Deletes a playlist. + /// + /// The interaction context. + /// The name of the playlist to delete. + [SlashCommand("delete", "Delete a playlist")] + public static async Task DeletePlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to delete", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist delete")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Renames a playlist. + /// + /// The interaction context. + /// The name of the playlist to rename. + /// The new name for the playlist. + [SlashCommand("rename", "Rename a playlist")] + public static async Task RenamePlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to rename", true)] string playlist, [Option("name", "New name for playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist rename")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Clears all entries from a playlist. + /// + /// The interaction context. + /// The name of the playlist to clear. + [SlashCommand("clear", "Clear all entries from a playlist")] + public static async Task ClearPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to clear", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist clear")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Plays a playlist or adds the songs to the queue. + /// + /// The interaction context. + /// The name of the playlist to play. + [SlashCommand("play", "Play a playlist/Add the songs to the queue")] + public static async Task PlayPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to play", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist play")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs new file mode 100644 index 00000000..4941564f --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs @@ -0,0 +1,99 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Playlist; + +/// +/// Contains commands for managing songs in playlists. +/// +public partial class PlaylistCommands +{ + /// + /// Provides commands for managing songs in playlists. + /// + [SlashCommandGroup("song", "Song management"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class SongCommands : ApplicationCommandsModule + { + /// + /// Adds a song to a playlist. + /// + /// The interaction context. + /// The name of the playlist to add the song to. + /// The URL or name of the song to add. + [SlashCommand("add", "Add a song to a playlist")] + public static async Task AddSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("url_or_name", "Url or name of song to add")] + string song + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Add song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Inserts a song into a playlist at a chosen position. + /// + /// The interaction context. + /// The name of the playlist to add the song to. + /// The position to insert the song at. + /// The URL or name of the song to add. + [SlashCommand("insert_at", "Insert a song into a playlist at a chosen position")] + public static async Task InsertSongAtAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("position", "Position to insert song at", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string posi, + [Option("url_or_name", "Url or name of song to add")] + string song + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Insert song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Moves a song to a specific position in a playlist. + /// + /// The interaction context. + /// The name of the playlist to move the song within. + /// The position to move the song from. + /// The position to move the song to. + [SlashCommand("move", "Move a song to a specific position in your playlist")] + public static async Task MoveSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string oldposi, + [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string newposi + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Move song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Removes a song from a playlist. + /// + /// The interaction context. + /// The name of the playlist to remove the song from. + /// The song to remove. + [SlashCommand("remove", "Remove a song from a playlist")] + public static async Task RemoveSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string posi + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Remove song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.cs new file mode 100644 index 00000000..fdc20e90 --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.cs @@ -0,0 +1,9 @@ +using MikuSharp.Attributes; + +namespace MikuSharp.Commands.Playlist; + +/// +/// The playlist commands +/// +[SlashCommandGroup("playlist", "Playlist commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), EnsureLavalinkSession] +public partial class PlaylistCommands : ApplicationCommandsModule; diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs deleted file mode 100644 index c6a7d064..00000000 --- a/MikuSharp/Commands/Utility.cs +++ /dev/null @@ -1,220 +0,0 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Exceptions; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Enums; -using DisCatSharp.Interactivity.Extensions; - -using Kitsu.Anime; -using Kitsu.Manga; - -using Microsoft.Extensions.Logging; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("utility", "Utilities")] -internal class Utility : ApplicationCommandsModule -{ - [SlashCommandGroup("am", "Anime & Mange")] - internal class AnimeMangaUtility : ApplicationCommandsModule - { - [SlashCommand("anime_search", "Search for an anime")] - public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Anime.GetAnimeAsync(search_query); - var emb = new DiscordEmbedBuilder(); - List res = new(); - List ress = new(); - foreach (var aa in a.Data) - { - emb.WithColor(new DiscordColor(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis.Length != 0) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype.Length != 0) - emb.AddField(new DiscordEmbedField("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.EpisodeCount != null) - emb.AddField(new DiscordEmbedField("Episodes", $"{aa.Attributes.EpisodeCount}", true)); - if (aa.Attributes.EpisodeLength != null) - emb.AddField(new DiscordEmbedField("Length", $"{aa.Attributes.EpisodeLength}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new DiscordEmbedField("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new DiscordEmbedField("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new DiscordEmbedField("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new DiscordEmbedField("Score", $"{aa.Attributes.AverageRating}", true)); - emb.AddField(new DiscordEmbedField("NSFW", $"{aa.Attributes.Nsfw}", true)); - if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); - res.Add(emb); - emb = new DiscordEmbedBuilder(); - } - res.Sort((x, y) => x.Title.CompareTo(y.Title)); - int i = 1; - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new Page(embed: aa)); - i++; - } - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); - } - } - - [SlashCommand("manga_search", "Search for an manga")] - public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Manga.GetMangaAsync(search_query); - var emb = new DiscordEmbedBuilder(); - List res = new(); - List ress = new(); - foreach (var aa in a.Data) - { - emb.WithColor(new DiscordColor(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis != null) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype != null) - emb.AddField(new DiscordEmbedField("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new DiscordEmbedField("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new DiscordEmbedField("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new DiscordEmbedField("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new DiscordEmbedField("Score", $"{aa.Attributes.AverageRating}", true)); - if (aa.Attributes.CoverImage?.Small != null) - emb.WithThumbnail(aa.Attributes.CoverImage.Small); - emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - res.Add(emb); - emb = new DiscordEmbedBuilder(); - } - res.Sort((x, y) => x.Title.CompareTo(y.Title)); - int i = 1; - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new Page(embed: aa)); - i++; - } - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); - } - } - } - - [SlashCommandGroup("discord", "Discord Utilities")] - internal class DiscordUtility : ApplicationCommandsModule - { - [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); - - [SlashCommand("server_info", "Get information about the server")] - public static async Task GuildInfoAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (ctx.Guild == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); - return; - } - - var members = await ctx.Guild.GetAllMembersAsync(); - var bots = members.Where(x => x.IsBot).Count(); - - var emb = new DiscordEmbedBuilder(); - emb.WithTitle(ctx.Guild.Name); - emb.WithColor(new DiscordColor(0212255)); - emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new DiscordEmbedField("Owner", ctx.Guild.Owner.Mention, true)); - emb.AddField(new DiscordEmbedField("Language", ctx.Guild.PreferredLocale, true)); - emb.AddField(new DiscordEmbedField("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new DiscordEmbedField("Created At", Formatter.Timestamp(ctx.Guild.CreationTimestamp, TimestampFormat.LongDateTime), true)); - emb.AddField(new DiscordEmbedField("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new DiscordEmbedField("Members (Bots)", $"{members.Count} ({bots})", true)); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("user_info", "Get information about a user")] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (user == null) - user = ctx.User; - - DiscordMember? member = null; - if (ctx.Guild != null) - { - try - { - member = await user.ConvertToMember(ctx.Guild); - } - catch (NotFoundException) { } - } - - var emb = new DiscordEmbedBuilder(); - emb.WithColor(new DiscordColor(0212255)); - emb.WithTitle("User Info"); - emb.AddField(new DiscordEmbedField("Username", $"{user.Username}#{user.Discriminator}", true)); - if (member != null) - if (member.DisplayName != user.Username) - emb.AddField(new DiscordEmbedField("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new DiscordEmbedField("ID", $"{user.Id}", true)); - emb.AddField(new DiscordEmbedField("Account Creation", $"{Formatter.Timestamp(user.CreationTimestamp)}", true)); - if (member != null) - emb.AddField(new DiscordEmbedField("Join Date", $"{Formatter.Timestamp(member.JoinedAt)}", true)); - emb.WithThumbnail(user.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - string wat = "You have to execute this command in a server!"; - if (ctx.Guild != null && ctx.Guild.Emojis.Any()) - { - wat = "**Emojies:** "; - foreach (var em in ctx.Guild.Emojis.Values) - { - wat += em + " "; - } - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); - } - } -} diff --git a/MikuSharp/Commands/UtilityCommands.cs b/MikuSharp/Commands/UtilityCommands.cs new file mode 100644 index 00000000..d828f546 --- /dev/null +++ b/MikuSharp/Commands/UtilityCommands.cs @@ -0,0 +1,126 @@ +using Kitsu.Anime; +using Kitsu.Manga; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("utility", "Utilities")] +internal class UtilityCommands : ApplicationCommandsModule +{ + [SlashCommandGroup("am", "Anime & Mange"), DeferResponseAsync] + internal class AnimeMangaUtility : ApplicationCommandsModule + { + [SlashCommand("anime_search", "Search for an anime")] + public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Anime.GetAnimeAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis.Length is not 0) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype.Length is not 0) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.EpisodeCount is not null) + emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); + if (aa.Attributes.EpisodeLength is not null) + emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); + if (aa.Attributes.StartDate is not null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate is not null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating is not null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating is not null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); + if (aa.Attributes.CoverImage?.Small is not null) + emb.WithThumbnail(aa.Attributes.CoverImage.Small); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild is not null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); + } + } + + [SlashCommand("manga_search", "Search for an manga")] + public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Manga.GetMangaAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis is not null) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype is not null) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.StartDate is not null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate is not null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating is not null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating is not null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + if (aa.Attributes.CoverImage?.Small is not null) + emb.WithThumbnail(aa.Attributes.CoverImage.Small); + emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild is not null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); + } + } + } +} diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs deleted file mode 100644 index 2f5a97ac..00000000 --- a/MikuSharp/Commands/Weeb.cs +++ /dev/null @@ -1,287 +0,0 @@ -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using HeyRed.Mime; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Utilities; - -using Newtonsoft.Json; - -using System; -using System.IO; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("weeb", "Weeb Stuff!", dmPermission: false)] -internal class Weeb : ApplicationCommandsModule -{ - [SlashCommand("awooify", "Awooify your or someones avatar!")] - public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) - { - await ctx.DeferAsync(false); - var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.message).Build())); - } - - [SlashCommand("diva", "Radnom PJD Loading image")] - public static async Task DivaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/diva")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("gumi", "Random Gumi image")] - public static async Task GumiPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/gumi")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("kaito", "Random Kaito image")] - public static async Task KaitoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/kaito")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("len", "Random Len image")] - public static async Task KLenPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/len")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("luka", "Random Luka image")] - public static async Task LukaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/luka")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("meiko", "Random Meiko image")] - public static async Task MeikoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("miku", "Random Miku image")] - public static async Task HMikuPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/miku")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("neko", "Get a random neko image")] - public static async Task Cat(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgURL.Url))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("rin", "Random Rin image")] - public static async Task KRinPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/rin")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("teto", "Random Teto image")] - public static async Task KTetoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/teto")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } -} diff --git a/MikuSharp/Commands/WeebCommands.cs b/MikuSharp/Commands/WeebCommands.cs new file mode 100644 index 00000000..f59a31f1 --- /dev/null +++ b/MikuSharp/Commands/WeebCommands.cs @@ -0,0 +1,116 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("weeb", "Weeb Stuff!", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] +internal class WeebCommands : ApplicationCommandsModule +{ + [SlashCommand("diva", "Radnom PJD Loading image")] + public static async Task DivaPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/diva")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("gumi", "Random Gumi image")] + public static async Task GumiPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("kaito", "Random Kaito image")] + public static async Task KaitoPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("len", "Random Len image")] + public static async Task KLenPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/len")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("luka", "Random Luka image")] + public static async Task LukaPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/luka")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("meiko", "Random Meiko image")] + public static async Task MeikoPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/meiko")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("miku", "Random Miku image")] + public static async Task HMikuPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/miku")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("rin", "Random Rin image")] + public static async Task KRinPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/rin")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } + + [SlashCommand("teto", "Random Teto image")] + public static async Task KTetoPic(InteractionContext ctx) + { + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) + { + await ctx.ImageRespondWithErrorAsync(); + return; + } + + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); + } +} diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index d945420b..5ccd537a 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -1,30 +1,28 @@ -using System.Collections.Generic; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public class Durl2 { - public int order { get; set; } - public int length { get; set; } - public int size { get; set; } - public string ahead { get; set; } - public string vhead { get; set; } - public string url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } } public class BiliJson { - public string from { get; set; } - public string result { get; set; } - public int quality { get; set; } - public string format { get; set; } - public int timelength { get; set; } - public string accept_format { get; set; } - public List accept_description { get; set; } - public List accept_quality { get; set; } - public int video_codecid { get; set; } - public bool video_project { get; set; } - public string seek_param { get; set; } - public string seek_type { get; set; } - public List durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public bool VideoProject { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index 4cd13f0b..22d099d7 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -1,45 +1,42 @@ -using System.Collections.Generic; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public class BiliPlayinfo { - public int code { get; set; } - public string message { get; set; } - public int ttl { get; set; } - public Data data { get; set; } - public string session { get; set; } - public VideoFrame videoFrame { get; set; } + public int Code { get; set; } + public string Message { get; set; } + public int Ttl { get; set; } + public Data Data { get; set; } + public string Session { get; set; } + public VideoFrame VideoFrame { get; set; } } public class Durl { - public int order { get; set; } - public int length { get; set; } - public int size { get; set; } - public string ahead { get; set; } - public string vhead { get; set; } - public string url { get; set; } - public object backup_url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } + public object BackupUrl { get; set; } } public class Data { - public string from { get; set; } - public string result { get; set; } - public string message { get; set; } - public int quality { get; set; } - public string format { get; set; } - public int timelength { get; set; } - public string accept_format { get; set; } - public List accept_description { get; set; } - public List accept_quality { get; set; } - public int video_codecid { get; set; } - public string seek_param { get; set; } - public string seek_type { get; set; } - public List durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public string Message { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } public class VideoFrame -{ -} +{ } diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index d30aaf35..5553c499 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -1,10 +1,12 @@ -using Newtonsoft.Json; +namespace MikuSharp.Entities; -namespace MikuSharp.Entities; - -public partial class BotConfig +public sealed class BotConfig { +#if DEBUG + [JsonProperty("discordTokenDev")] +#else [JsonProperty("discordToken")] +#endif public string DiscordToken { get; set; } [JsonProperty("discordBotListToken")] @@ -32,7 +34,7 @@ public partial class BotConfig public NndConfig NndConfig { get; set; } } -public partial class DatabaseConfig +public sealed class DatabaseConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -47,7 +49,7 @@ public partial class DatabaseConfig public string Database { get; set; } } -public partial class LavalinkConfig +public sealed class LavalinkConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -59,7 +61,7 @@ public partial class LavalinkConfig public int Port { get; set; } } -public partial class NndConfig +public sealed class NndConfig { [JsonProperty("mail")] public string Mail { get; set; } diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index df9eddc3..198da4ed 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -1,7 +1,10 @@ -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; -public class DogCeo +public sealed class DogCeo : ImgData { - public string status { get; set; } - public string message { get; set; } + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } } diff --git a/MikuSharp/Entities/Entry.cs b/MikuSharp/Entities/Entry.cs deleted file mode 100644 index 2ed6c68e..00000000 --- a/MikuSharp/Entities/Entry.cs +++ /dev/null @@ -1,17 +0,0 @@ -/*using DisCatSharp.Lavalink; - -using System; - -namespace MikuSharp.Entities; - -public class Entry -{ - public LavalinkTrack track { get; protected set; } - public DateTimeOffset additionDate { get; protected set; } - public Entry(LavalinkTrack t, DateTimeOffset addtime) - { - track = t; - additionDate = addtime; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/Games/CointossGame.cs b/MikuSharp/Entities/Games/CointossGame.cs new file mode 100644 index 00000000..bf7a40ea --- /dev/null +++ b/MikuSharp/Entities/Games/CointossGame.cs @@ -0,0 +1,77 @@ +using MikuSharp.Utilities; + +namespace MikuSharp.Entities.Games; + +/// +/// Represents a coin toss game. +/// +/// The context. +public sealed class CointossGame(InteractionContext context) +{ + /// + /// Gets the interaction context. + /// + public InteractionContext Context { get; } = context; + + /// + /// Gets the dictionary of coin toss side emojis. + /// + public Dictionary CoinTossSideEmojis + => new() + { + { CointossSide.Heads, DiscordEmoji.FromApplicationEmote(this.Context.Client, 1338091202188279818) }, + { CointossSide.Tails, DiscordEmoji.FromApplicationEmote(this.Context.Client, 1338091187009228882) } + }; + + /// + /// Gets the dictionary of coin toss side images. + /// + public Dictionary CoinTossSideImages { get; } = new() + { + { CointossSide.Heads, "https://miku-cdn.aitsys.dev/assets/miku/cointoss/heads.png" }, + { CointossSide.Tails, "https://miku-cdn.aitsys.dev/assets/miku/cointoss/tails.png" } + }; + + /// + /// Gets or sets the result of the coin toss. + /// + public CointossSide Result { get; internal set; } + + /// + /// Tosses the coin and sets the result. + /// + /// The current instance of . + public CointossGame TossCoin() + { + Random random = new(); + this.Result = Enum.GetValues()[random.Next(0, 20) > 10 ? 1 : 0]; + return this; + } + + /// + /// Tries to build and send a components V2 cointoss message. + /// + /// Whether the message was sent successfully. + public async Task SendCointossMessageAsync() + { + DiscordSectionComponent sectionComponent = new([DiscordExtensionMethods.EmptyComponent, new("And the winner..."), new($"### {this.Result}")]); + sectionComponent.WithThumbnailComponent(this.CoinTossSideImages[this.Result], this.Result.ToString()); + await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([sectionComponent]))); + } +} + +/// +/// Represents the sides of a coin. +/// +public enum CointossSide +{ + /// + /// Heads side of the coin. + /// + Heads, + + /// + /// Tails side of the coin. + /// + Tails +} diff --git a/MikuSharp/Entities/Games/EightBallGame.cs b/MikuSharp/Entities/Games/EightBallGame.cs new file mode 100644 index 00000000..3ab5ed9f --- /dev/null +++ b/MikuSharp/Entities/Games/EightBallGame.cs @@ -0,0 +1,69 @@ +using MikuSharp.Utilities; + +namespace MikuSharp.Entities.Games; + +/// +/// Represents the 8ball game. +/// +public sealed class EightBallGame +{ + /// + /// Gets a random response from the 8ball. + /// + /// The user question. + /// The response. + public EightBallGame(string userQuestion) + { + Random random = new(); + var responseList = Responses[random.Next(Responses.Count)].ToList(); + this.ChosenAnswer = responseList[random.Next(responseList.Count)]; + this.UserQuestion = userQuestion; + } + + /// + /// A list of negative responses that the 8ball can give. + /// + public static IEnumerable NegativeResponses { get; } = ["Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope."]; + + /// + /// A list of positive responses that the 8ball can give. + /// + public static IEnumerable PositiveResponses { get; } = ["It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor."]; + + /// + /// A list of neutral responses that the 8ball can give. + /// + public static IEnumerable NeutralResponse { get; } = ["Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance."]; + + /// + /// A list of all the responses that the 8ball can give. + /// + public static IReadOnlyList> Responses { get; } = [NegativeResponses, PositiveResponses, NeutralResponse]; + + /// + /// Gets the chosen answer. + /// + public string ChosenAnswer { get; } + + /// + /// Gets the user question. + /// + public string UserQuestion { get; } + + /// + /// Tries to build and send a components V2 8ball message. + /// + /// The context. + /// Whether the message was sent successfully. + public async Task Send8BallMessageAsync(InteractionContext ctx) + { + DiscordTextDisplayComponent question = new($"### Question\n{this.UserQuestion}"); + DiscordSectionComponent questionComponent = new([DiscordExtensionMethods.EmptyComponent, question]); + questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); + DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent answer = new($"### Answer\n{this.ChosenAnswer}"); + DiscordSectionComponent answerComponent = new([DiscordExtensionMethods.EmptyComponent, answer]); + answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## 8Ball"), new DiscordSeparatorComponent(true, SeparatorSpacingSize.Large), questionComponent, seperator, answerComponent]))); + } +} diff --git a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs new file mode 100644 index 00000000..8fb49074 --- /dev/null +++ b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs @@ -0,0 +1,178 @@ +using MikuSharp.Utilities; + +namespace MikuSharp.Entities.Games; + +/// +/// Represents a rock-paper-scissors game. +/// +public static class RockPaperScissorsGame +{ + /// + /// The rock–paper–scissors assets. + /// + public static IEnumerable Assets + => [new(RockPaperScissorsChoiceType.Rock, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-rock-88661.png", "Rock by Studio Fibonacci from Noun Project (CC BY 3.0)"), new(RockPaperScissorsChoiceType.Paper, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-paper-88662.png", "Paper by Studio Fibonacci from Noun Project (CC BY 3.0)"), new(RockPaperScissorsChoiceType.Scissors, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-scissors-88666.png", "Scissors by Studio Fibonacci from Noun Project (CC BY 3.0)")]; + + /// + /// Plays a round of rock–paper–scissors against the user. + /// + /// The user's choice. + /// A representing the outcome. + public static RockPaperScissorsResponse ResolveRps(this RockPaperScissorsChoiceType userChoice, DiscordUser player) + { + var random = new Random(); + var values = Enum.GetValues(); + var computerChoice = (RockPaperScissorsChoiceType)values.GetValue(random.Next(values.Length))!; + + return new(player, userChoice, computerChoice, userChoice == computerChoice + ? RockPaperScissorsWinType.Tie + : (userChoice is RockPaperScissorsChoiceType.Rock && computerChoice is RockPaperScissorsChoiceType.Scissors) || + (userChoice is RockPaperScissorsChoiceType.Paper && computerChoice is RockPaperScissorsChoiceType.Rock) || + (userChoice is RockPaperScissorsChoiceType.Scissors && computerChoice is RockPaperScissorsChoiceType.Paper) + ? RockPaperScissorsWinType.User + : RockPaperScissorsWinType.Computer); + } +} + +/// +/// Represents a rock-paper-scissors asset. +/// +/// The type. +/// The image URL. +/// The creative commons license. +public sealed class RockPaperScissorsAsset(RockPaperScissorsChoiceType choiceType, string imgUrl, string ccl) +{ + /// + /// The type. + /// + public RockPaperScissorsChoiceType ChoiceType { get; } = choiceType; + + /// + /// The image URL. + /// + public string ImageUrl { get; } = imgUrl; + + /// + /// The creative commons license. + /// + public string CreativeCommonsLicense { get; } = ccl; + + /// + /// Maps a to a . + /// + /// The choice type. + /// The mapped . + public static implicit operator RockPaperScissorsAsset(RockPaperScissorsChoiceType type) + => RockPaperScissorsGame.Assets.First(x => x.ChoiceType == type); +} + +/// +/// Constructs a new . +/// +/// The user choice asset. +/// The computer choice asset. +/// The win type. +public sealed class RockPaperScissorsResponse(DiscordUser player, RockPaperScissorsAsset userChoiceAsset, RockPaperScissorsAsset computerChoiceAsset, RockPaperScissorsWinType winType) +{ + /// + /// Gets the computer's choice asset. + /// + public RockPaperScissorsAsset UserChoiceAsset { get; } = userChoiceAsset; + + /// + /// Gets the computer's choice asset. + /// + public RockPaperScissorsAsset ComputerChoiceAsset { get; } = computerChoiceAsset; + + /// + /// Gets the result. + /// + public RockPaperScissorsWinType WinType { get; } = winType; + + /// + /// Gets the winner asset. + /// + public (string ImageUrl, string CreativeCommonsLicense) WinnerAsset + => this.WinType is RockPaperScissorsWinType.Computer or RockPaperScissorsWinType.Tie + ? (this.ComputerChoiceAsset.ImageUrl, this.ComputerChoiceAsset.CreativeCommonsLicense) + : (this.UserChoiceAsset.ImageUrl, this.UserChoiceAsset.CreativeCommonsLicense); + + public DiscordUser Player { get; } = player; + + /// + /// Tries to build and send a components V2 rock-paper-scissors message. + /// + /// The context. + /// Whether the message was sent successfully. + public async Task SendRpsMessageAsync(InteractionContext ctx) + { + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + DiscordTextDisplayComponent userChoiceComponent = new($"### {ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType}"); + DiscordSectionComponent userChoiceSection = new([DiscordExtensionMethods.EmptyComponent, userChoiceComponent]); + userChoiceSection.WithThumbnailComponent(ctx.User.AvatarUrl, "User Avatar"); + DiscordSeparatorComponent seperator1 = new(false, SeparatorSpacingSize.Large); + DiscordTextDisplayComponent computerChoiceComponent = new($"### I choose {this.ComputerChoiceAsset.ChoiceType}"); + DiscordSectionComponent computerChoiceSection = new([DiscordExtensionMethods.EmptyComponent, computerChoiceComponent]); + computerChoiceSection.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl, "Bot Avatar"); + DiscordSeparatorComponent seperator2 = new(true, SeparatorSpacingSize.Large); + DiscordTextDisplayComponent resultTextComponent = new($"### {this}"); + DiscordSectionComponent resultSectionComponent = new([DiscordExtensionMethods.EmptyComponent, resultTextComponent]); + resultSectionComponent.WithThumbnailComponent(this.WinnerAsset.ImageUrl, this.WinnerAsset.CreativeCommonsLicense); + builder.AddComponents(new DiscordContainerComponent([userChoiceSection, seperator1, computerChoiceSection, seperator2, resultSectionComponent])); + builder.WithAllowedMention(new UserMention(ctx.User)); + await ctx.EditResponseAsync(builder); + } + + /// + public override string? ToString() + => this.WinType switch + { + RockPaperScissorsWinType.User => $"{this.Player.Mention} wins :3", + RockPaperScissorsWinType.Computer => "I win ^~^", + RockPaperScissorsWinType.Tie => "It's a tie O.o", + _ => null + }; +} + +/// +/// Represents a rock paper scissors type. +/// +public enum RockPaperScissorsChoiceType +{ + /// + /// The rock. + /// + Rock, + + /// + /// The paper. + /// + Paper, + + /// + /// The scissors. + /// + Scissors +} + +/// +/// Represents a rock paper scissors win type. +/// +public enum RockPaperScissorsWinType +{ + /// + /// The user wins. + /// + User, + + /// + /// The computer wins. + /// + Computer, + + /// + /// It's a tie. + /// + Tie +} diff --git a/MikuSharp/Entities/Guild.cs b/MikuSharp/Entities/Guild.cs deleted file mode 100644 index 3067ad66..00000000 --- a/MikuSharp/Entities/Guild.cs +++ /dev/null @@ -1,35 +0,0 @@ -/*using System; -using System.Threading.Tasks; - -namespace MikuSharp.Entities; - -public class Guild -{ - public int shardId { get; set; } - //CustomPrefix stuff - public MusicInstance musicInstance { get; set; } - public Task AloneCheckThread { get; set; } - - public Guild(int id, MusicInstance mi = null) - { - shardId = id; - musicInstance = mi; - } - - public async Task CheckAlone() - { - while (DateTime.UtcNow.Subtract(musicInstance.aloneTime).Minutes != 5 && !musicInstance.aloneCTS.IsCancellationRequested) - { - await Task.Delay(1000); - if (musicInstance == null || musicInstance.guildConnection == null) - return; - } - if (DateTime.UtcNow.Subtract(musicInstance.aloneTime).Minutes == 5 && !musicInstance.aloneCTS.IsCancellationRequested) - { - await Task.Run(async () => await musicInstance.guildConnection.DisconnectAsync()); - await Task.Delay(500); - musicInstance = null; - } - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/ImgData.cs b/MikuSharp/Entities/ImgData.cs new file mode 100644 index 00000000..56e7cf12 --- /dev/null +++ b/MikuSharp/Entities/ImgData.cs @@ -0,0 +1,19 @@ +namespace MikuSharp.Entities; + +/// +/// Represents image data. +/// +public class ImgData +{ + /// + /// Gets the data. + /// + [JsonIgnore] + public MemoryStream Data { get; set; } + + /// + /// Gets the file type. + /// + [JsonIgnore] + public string FileType { get; set; } +} diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs deleted file mode 100644 index 2d306247..00000000 --- a/MikuSharp/Entities/Img_Data.cs +++ /dev/null @@ -1,13 +0,0 @@ -using DisCatSharp.Entities; - -using System.IO; - -namespace MikuSharp.Entities; - -public class Img_Data -{ - public Stream Data { get; set; } - public string Filetype { get; set; } - - public DiscordEmbed Embed { get; set; } -} diff --git a/MikuSharp/Entities/KsoftSiImage.cs b/MikuSharp/Entities/KsoftSiImage.cs new file mode 100644 index 00000000..41c3dd76 --- /dev/null +++ b/MikuSharp/Entities/KsoftSiImage.cs @@ -0,0 +1,31 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a KSoft.si image. +/// +public sealed class KsoftSiImage : ImgData +{ + /// + /// Gets the url. + /// + [JsonProperty("url")] + public string Url { get; set; } + + /// + /// Gets the snowflake. + /// + [JsonProperty("snowflake")] + public string Snowflake { get; set; } + + /// + /// Gets whether the image is NSFW. + /// + [JsonProperty("nsfw")] + public bool Nsfw { get; set; } + + /// + /// Gets the tag. + /// + [JsonProperty("tag")] + public string Tag { get; set; } +} diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs deleted file mode 100644 index adb3f0d1..00000000 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MikuSharp.Entities; - -public class KsoftSiRanImg : Img_Data -{ - public string url { get; set; } - public string snowflake { get; set; } - public bool nsfw { get; set; } - public string tag { get; set; } -} diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs deleted file mode 100644 index 63e72ec8..00000000 --- a/MikuSharp/Entities/MeekMoe.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MikuSharp.Entities; - -public class MeekMoe -{ - public string url { get; set; } - public string creator { get; set; } -} diff --git a/MikuSharp/Entities/MeekMoeImage.cs b/MikuSharp/Entities/MeekMoeImage.cs new file mode 100644 index 00000000..29ed536e --- /dev/null +++ b/MikuSharp/Entities/MeekMoeImage.cs @@ -0,0 +1,19 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a meek moe image response. +/// +public sealed class MeekMoeImage : ImgData +{ + /// + /// Gets the url. + /// + [JsonProperty("url")] + public required string Url { get; set; } + + /// + /// Gets the creator. + /// + [JsonProperty("creator", NullValueHandling = NullValueHandling.Ignore)] + public string? Creator { get; set; } +} diff --git a/MikuSharp/Entities/MusicInstance.cs b/MikuSharp/Entities/MusicInstance.cs deleted file mode 100644 index 4e00e928..00000000 --- a/MikuSharp/Entities/MusicInstance.cs +++ /dev/null @@ -1,368 +0,0 @@ -/*using DisCatSharp; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; - -using FluentFTP; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Enums; -using MikuSharp.Events; -using MikuSharp.Utilities; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace MikuSharp.Entities; - -public class MusicInstance -{ - public int shardID { get; set; } - public DiscordChannel usedChannel { get; set; } - public DiscordChannel voiceChannel { get; set; } - public Playstate playstate { get; set; } - public RepeatMode repeatMode { get; set; } - public int repeatAllPos { get; set; } - public ShuffleMode shuffleMode { get; set; } - public DateTime aloneTime { get; set; } - public CancellationTokenSource aloneCTS { get; set; } - public LavalinkNodeConnection nodeConnection { get; set; } - public LavalinkGuildConnection guildConnection { get; set; } - public QueueEntry currentSong { get; set; } - public QueueEntry lastSong { get; set; } - - public MusicInstance(LavalinkNodeConnection node, int shard) - { - shardID = shard; - nodeConnection = node; - usedChannel = null; - playstate = Playstate.NotPlaying; - repeatMode = RepeatMode.Off; - repeatAllPos = 0; - shuffleMode = ShuffleMode.Off; - } - - public async Task ConnectToChannel(DiscordChannel channel) - { - switch (channel.Type) - { - case ChannelType.Voice: - { - guildConnection = await nodeConnection.ConnectAsync(channel); - voiceChannel = channel; - return guildConnection; - } - default: return null; - } - } - public async Task QueueSong(string n, InteractionContext ctx, int pos = -1) - { - var queue = await Database.GetQueueAsync(ctx.Guild); - var inter = ctx.Client.GetInteractivity(); - if (n.ToLower().StartsWith("http://nicovideo.jp") - || n.ToLower().StartsWith("http://sp.nicovideo.jp") - || n.ToLower().StartsWith("https://nicovideo.jp") - || n.ToLower().StartsWith("https://sp.nicovideo.jp") - || n.ToLower().StartsWith("http://www.nicovideo.jp") - || n.ToLower().StartsWith("https://www.nicovideo.jp")) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); - var split = n.Split("/".ToCharArray()); - var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; - FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - client.Connect(); - if (!client.FileExists($"{nndID}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading")); - client.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); - } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); - if (pos == -1) await Database.AddToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString); - else await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); - } - else if (n.ToLower().StartsWith("https://www.bilibili.com") - || n.ToLower().StartsWith("http://www.bilibili.com")) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing Bilibili Video...").AsEphemeral()); - n = n.Replace("https://www.bilibili.com/", ""); - n = n.Replace("http://www.bilibili.com/", ""); - var split = n.Split("/".ToCharArray()); - if (!split.Contains("video")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Failure")); - return null; - } - var nndID = split[1].Split("?")[0]; - FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - client.Connect(); - if (!client.FileExists($"{nndID}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetBilibiliAsync(nndID, msg.Id); - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); - client.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); - } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); - } - else if (n.StartsWith("http://") | n.StartsWith("https://")) - { - try - { - var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> Playlist is set to private or unlisted\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); - return null; - }; - case LavalinkLoadResultType.PlaylistLoaded: - { - if (s.PlaylistInfo.SelectedTrack == -1) - { - List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), - new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() - .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ToList()); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - else - { - List buttons = new(3) - { - new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), - new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Link with Playlist detected!") - .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); - if (pos == -1) await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).TrackString); - else await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); - } - else if (resp.Result.Id == "all") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks); - else - { - s.Tracks.Reverse(); - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks, pos); - } - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - }; - default: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.First().TrackString); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks.First()); - }; - } - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - return null; - } - } - else - { - var type = LavalinkSearchType.Youtube; - if (n.StartsWith("ytsearch:")) - { - n = n.Replace("ytsearch:", ""); - type = LavalinkSearchType.Youtube; - } - else if (n.StartsWith("scsearch:")) - { - n = n.Replace("ytsearch:", ""); - type = LavalinkSearchType.SoundCloud; - } - else if (n.StartsWith("spsearch:")) - { - n = n.Replace("spsearch:", ""); - type = LavalinkSearchType.Spotify; - } - else if (n.StartsWith("amsearch:")) - { - n = n.Replace("amsearch:", ""); - type = LavalinkSearchType.AppleMusic; - } - - var s = await nodeConnection.Rest.GetTracksAsync(n, type); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - ctx.Client.Logger.LogDebug("Load failed"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - ctx.Client.Logger.LogDebug("No matches"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); - return null; - }; - default: - { - ctx.Client.Logger.LogDebug("Found something"); - int leng = s.Tracks.Count; - if (leng > 5) leng = 5; - List selectOptions = new(leng); - var em = new DiscordEmbedBuilder() - .WithTitle("Results!") - .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); - for (int i = 0; i < leng; i++) - { - em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); - selectOptions.Add(new DiscordStringSelectComponentOption(s.Tracks.ElementAt(i).Title, i.ToString(), $"by {s.Tracks.ElementAt(i).Author}. Length: {s.Tracks.ElementAt(i).Length}")); - } - DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); - var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); - return null; - } - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - var trackSelect = Convert.ToInt32(resp.Result.Values.First()); - var track = s.Tracks.ElementAt(trackSelect); - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.TrackString); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(s.PlaylistInfo, track); - }; - } - } - } - - public async Task PlaySong() - { - var queue = await Database.GetQueueAsync(voiceChannel.Guild); - var cur = lastSong; - if (queue.Count != 1 && repeatMode == RepeatMode.All) - repeatAllPos++; - if (repeatAllPos >= queue.Count) - repeatAllPos = 0; - currentSong = shuffleMode == ShuffleMode.Off ? queue[0] : queue[new Random().Next(0, queue.Count)]; - if (repeatMode == RepeatMode.All) - currentSong = queue[repeatAllPos]; - if (repeatMode == RepeatMode.On) - currentSong = cur; - MikuBot.ShardedClient.Logger.LogDebug(currentSong?.track.TrackString); - guildConnection.PlaybackFinished += Lavalink.LavalinkTrackFinish; - playstate = Playstate.Playing; - await Task.Run(async () => await guildConnection.PlayAsync(currentSong.track)); - return currentSong; - } -} - -// B/S(`・ω・´) ❤️ (´ω`)U/C -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs new file mode 100644 index 00000000..f2d61189 --- /dev/null +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -0,0 +1,31 @@ +using MikuSharp.Enums; +using MikuSharp.Utilities; + +namespace MikuSharp.Entities; + +internal sealed class MusicQueueEntry : IQueueEntry +{ + /// + public async Task BeforePlayingAsync(LavalinkGuildPlayer player) + { + return await player.GuildId.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title.Bold()} from {this.Track.Info.Author.Italic()}")); + return true; + }, defaultValue: false); + } + + /// + public async Task AfterPlayingAsync(LavalinkGuildPlayer player) + { + await player.GuildId.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); + }); + } + + /// + public LavalinkTrack Track { get; set; } +} diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs new file mode 100644 index 00000000..1587e0d6 --- /dev/null +++ b/MikuSharp/Entities/MusicSession.cs @@ -0,0 +1,123 @@ +using DisCatSharp.Exceptions; + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using MikuSharp.Enums; + +namespace MikuSharp.Entities; + +/// +/// Represents a music session. +/// +/// The channel the music session is for. +/// The guild the music session is for. +/// The Lavalink session. +public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, LavalinkSession lavalinkSession) +{ + /// + /// Gets the current channel. + /// + public DiscordChannel CurrentChannel { get; } = channel; + + /// + /// Gets the current guild. + /// + public DiscordGuild CurrentGuild { get; } = guild; + + /// + /// Gets the Lavalink session. + /// + public LavalinkSession LavalinkSession { get; } = lavalinkSession; + + /// + /// Gets the Lavalink guild player. + /// + public LavalinkGuildPlayer? LavalinkGuildPlayer { get; internal set; } + + /// + /// Gets the repeat mode. + /// + public RepeatMode RepeatMode { get; internal set; } = RepeatMode.None; + + /// + /// Gets the status message. + /// + public DiscordMessage? StatusMessage { get; internal set; } + + /// + /// Gets the play state. + /// + public PlaybackState PlaybackState { get; internal set; } = PlaybackState.Stopped; + + /// + /// Injects the player. + /// + /// The current music session. + public async Task InjectPlayerAsync() + { + this.LavalinkGuildPlayer = this.LavalinkSession.GetGuildPlayer(this.CurrentGuild)!; + this.LavalinkGuildPlayer.SetRepeatMode(this.RepeatMode); + await this.LavalinkGuildPlayer.SetVolumeAsync(20); + return this; + } + + /// + /// Updates the repeat mode. + /// + /// The new repeat mode. + /// The current music session. + public MusicSession UpdateRepeatMode(RepeatMode mode) + { + this.RepeatMode = mode; + this.LavalinkGuildPlayer?.SetRepeatMode(mode); + return this; + } + + /// + /// Updates the status message. + /// + /// The new status message. + /// The current music session. + public MusicSession UpdateStatusMessage(DiscordMessage message) + { + this.StatusMessage = message; + return this; + } + + /// + /// Updates the play state. + /// + /// The new play state. + /// The current music session. + public MusicSession UpdatePlaybackState(PlaybackState state) + { + this.PlaybackState = state; + return this; + } + + /// + /// Updates the status message. + /// + /// The new status message embed. + /// The current music session. + public async Task UpdateStatusMessageAsync(DiscordEmbed embed) + { + try + { + if (this.StatusMessage is not null) + await this.StatusMessage.DeleteAsync("Updating miku status"); + else + { + var messages = await this.CurrentChannel.GetMessagesAsync(50); + var mikuMessages = messages.Where(msg => msg.Author.Id == HatsuneMikuBot.ShardedClient.CurrentUser.Id).OrderByDescending(msg => msg.CreationTimestamp).ToList(); + var targetMessage = mikuMessages.FirstOrDefault(msg => msg.Embeds.Count is 1); + if (targetMessage is not null) + await targetMessage.DeleteAsync("Updating miku status"); + } + } + catch (NotFoundException) + { } + + return this.UpdateStatusMessage(await this.CurrentChannel.SendMessageAsync(embed)); + } +} diff --git a/MikuSharp/Entities/NekoBotImage.cs b/MikuSharp/Entities/NekoBotImage.cs new file mode 100644 index 00000000..374a9721 --- /dev/null +++ b/MikuSharp/Entities/NekoBotImage.cs @@ -0,0 +1,33 @@ +using System.Net; + +namespace MikuSharp.Entities; + +/// +/// Represents a Nekobot response. +/// +public sealed class NekoBotImage : ImgData +{ + /// + /// Gets the message. + /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Include)] + public string Message { get; internal set; } + + /// + /// Gets the status. + /// + [JsonProperty("status")] + public HttpStatusCode Status { get; internal set; } + + /// + /// Gets whether the request was successful. + /// + [JsonProperty("success")] + public bool Success { get; internal set; } + + /// + /// Gets the version. + /// + [JsonProperty("version")] + public string Version { get; internal set; } +} diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs deleted file mode 100644 index 623ea038..00000000 --- a/MikuSharp/Entities/Nekobot.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MikuSharp.Entities; - -public class NekoBot : Img_Data -{ - public string message { get; set; } - public int status { get; set; } - public bool success { get; set; } -} diff --git a/MikuSharp/Entities/NekosLifeImage.cs b/MikuSharp/Entities/NekosLifeImage.cs new file mode 100644 index 00000000..eb937a3f --- /dev/null +++ b/MikuSharp/Entities/NekosLifeImage.cs @@ -0,0 +1,13 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a nekos.life response. +/// +public sealed class NekosLifeImage : ImgData +{ + /// + /// Gets the URL. + /// + [JsonProperty("url")] + public string Url { get; set; } +} diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs deleted file mode 100644 index f8053577..00000000 --- a/MikuSharp/Entities/Nekos_Life.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MikuSharp.Entities; - -public class Nekos_Life : Img_Data -{ - public string Url { get; set; } -} diff --git a/MikuSharp/Entities/Old/Entry.cs b/MikuSharp/Entities/Old/Entry.cs new file mode 100644 index 00000000..f40e341b --- /dev/null +++ b/MikuSharp/Entities/Old/Entry.cs @@ -0,0 +1,20 @@ +/*using System; + +using DisCatSharp.Lavalink.Entities; + +namespace MikuSharp.Entities; + +public class Entry +{ + public Entry(LavalinkTrack t, DateTimeOffset addtime) + { + this.Track = t; + this.AdditionDate = addtime; + } + + public LavalinkTrack Track { get; protected set; } + public DateTimeOffset AdditionDate { get; protected set; } +} +*/ + + diff --git a/MikuSharp/Entities/Old/Guild.cs b/MikuSharp/Entities/Old/Guild.cs new file mode 100644 index 00000000..af21d977 --- /dev/null +++ b/MikuSharp/Entities/Old/Guild.cs @@ -0,0 +1,39 @@ +/*using System; +using System.Threading.Tasks; + +namespace MikuSharp.Entities; + +public class Guild +{ + public Guild(int id, MusicInstance? mi = null) + { + this.ShardId = id; + this.MusicInstance = mi; + } + + public int ShardId { get; set; } + + //CustomPrefix stuff + public MusicInstance? MusicInstance { get; set; } + public Task AloneCheckThread { get; set; } + + public async Task CheckAlone() + { + while (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes != 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) + { + await Task.Delay(1000); + if (this.MusicInstance?.GuildConnection is null) + return; + } + + if (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes == 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) + { + await Task.Run(async () => await this.MusicInstance.GuildConnection.DisconnectAsync()); + await Task.Delay(500); + this.MusicInstance = null; + } + } +} +*/ + + diff --git a/MikuSharp/Entities/Old/MusicInstance.cs b/MikuSharp/Entities/Old/MusicInstance.cs new file mode 100644 index 00000000..8dd2e781 --- /dev/null +++ b/MikuSharp/Entities/Old/MusicInstance.cs @@ -0,0 +1,409 @@ +/*using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Enums; +using DisCatSharp.Interactivity.Extensions; +using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; +using DisCatSharp.Lavalink.Enums; + +using FluentFTP; + +using Microsoft.Extensions.Logging; + +using MikuSharp.Enums; +using MikuSharp.Events; +using MikuSharp.Utilities; + +namespace MikuSharp.Entities; + +public class MusicInstance +{ + public MusicInstance(LavalinkSession node, int shard) + { + this.ShardId = shard; + this.Session = node; + this.UsedChannel = null; + this.Playstate = Playstate.NotPlaying; + this.RepeatMode = RepeatMode.Off; + this.RepeatAllPos = 0; + this.ShuffleMode = ShuffleMode.Off; + } + + public int ShardId { get; set; } + public DiscordChannel UsedChannel { get; set; } + public DiscordChannel VoiceChannel { get; set; } + public Playstate Playstate { get; set; } + public RepeatMode RepeatMode { get; set; } + public int RepeatAllPos { get; set; } + public ShuffleMode ShuffleMode { get; set; } + public DateTime AloneTime { get; set; } + public CancellationTokenSource AloneCts { get; set; } + public LavalinkSession Session { get; set; } + public LavalinkGuildPlayer GuildConnection { get; set; } + public QueueEntry CurrentSong { get; set; } + public QueueEntry LastSong { get; set; } + + public async Task ConnectToChannel(DiscordChannel channel) + { + switch (channel.Type) + { + case ChannelType.Voice: + { + this.GuildConnection = await this.Session.ConnectAsync(channel); + this.VoiceChannel = channel; + return this.GuildConnection; + } + default: + return null; + } + } + + public async Task QueueSong(string n, InteractionContext ctx, int pos = -1) + { + var queue = await Database.GetQueueAsync(ctx.Guild); + var inter = ctx.Client.GetInteractivity(); + + if (n.ToLower().StartsWith("http://nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("http://sp.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://sp.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("http://www.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://www.nicovideo.jp", StringComparison.Ordinal)) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); + var split = n.Split("/".ToCharArray()); + var nndId = split.First(x => x.StartsWith("sm", StringComparison.Ordinal) || x.StartsWith("nm", StringComparison.Ordinal)).Split("?")[0]; + FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + client.Connect(); + + if (!client.FileExists($"{nndId}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetNndAsync(n, nndId, msg.Id); + + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading")); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); + } + + var track = (await this.Session.Rest.LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); + } + + if (n.ToLower().StartsWith("https://www.bilibili.com", StringComparison.Ordinal) || n.ToLower().StartsWith("http://www.bilibili.com", StringComparison.Ordinal)) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing Bilibili Video...").AsEphemeral()); + n = n.Replace("https://www.bilibili.com/", ""); + n = n.Replace("http://www.bilibili.com/", ""); + var split = n.Split("/".ToCharArray()); + + if (!split.Contains("video")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Failure")); + return null; + } + + var nndId = split[1].Split("?")[0]; + FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + client.Connect(); + + if (!client.FileExists($"{nndId}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetBilibiliAsync(nndId, msg.Id); + + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); + } + + var track = (await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); + } + + if (n.StartsWith("http://", StringComparison.Ordinal) | n.StartsWith("https://", StringComparison.Ordinal)) + try + { + var s = await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync(n); + + switch (s.LoadType) + { + case LavalinkLoadResultType.Error: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load") + .WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + "> Playlist is set to private or unlisted\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral() + .AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); + return null; + } + ; + case LavalinkLoadResultType.Playlist: + { + var pl = s.GetResultAs(); + + if (pl.Info.SelectedTrack == -1) + { + List buttons = [new(ButtonStyle.Success, "yes", "Add entire playlist"), new(ButtonStyle.Primary, "no", "Don't add")]; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() + .WithDescription("Choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + + if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks); + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + else + { + List buttons = [new(ButtonStyle.Primary, "yes", "Add only referred song"), new(ButtonStyle.Success, "yes", "Add the entire playlist"), new(ButtonStyle.Danger, "no", "Cancel")]; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Link with Playlist detected!") + .WithDescription("Please choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + + if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {pl.Tracks.ElementAt(pl.Info.SelectedTrack).Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks.ElementAt(pl.Info.SelectedTrack)); + } + + if (resp.Result.Id == "all") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {pl.Info.Name}")); + + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); + else + { + pl.Tracks.Reverse(); + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks, pos); + } + + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) await this.PlaySong(); + return new(pl.Info, pl.Tracks); + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + ; + default: + { + var p = s.GetResultAs(); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {p.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, p.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, p); + } + ; + } + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + return null; + } + + var type = LavalinkSearchType.Youtube; + + if (n.StartsWith("ytsearch:", StringComparison.Ordinal)) + { + n = n.Replace("ytsearch:", ""); + type = LavalinkSearchType.Youtube; + } + else if (n.StartsWith("scsearch:", StringComparison.Ordinal)) + { + n = n.Replace("ytsearch:", ""); + type = LavalinkSearchType.SoundCloud; + } + else if (n.StartsWith("spsearch:", StringComparison.Ordinal)) + { + n = n.Replace("spsearch:", ""); + type = LavalinkSearchType.Spotify; + } + else if (n.StartsWith("amsearch:", StringComparison.Ordinal)) + { + n = n.Replace("amsearch:", ""); + type = LavalinkSearchType.AppleMusic; + } + + var trackLoadingResult = await this.Session.ConnectedPlayers[ctx.GuildId!.Value].LoadTracksAsync(type, n); + + switch (trackLoadingResult.LoadType) + { + case LavalinkLoadResultType.Error: + { + ctx.Client.Logger.LogDebug("Load failed"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load") + .WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + ctx.Client.Logger.LogDebug("No matches"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); + return null; + } + ; + default: + { + var s = trackLoadingResult.GetResultAs>(); + ctx.Client.Logger.LogDebug("Found something"); + var leng = s.Count; + if (leng > 5) leng = 5; + List selectOptions = new(leng); + var em = new DiscordEmbedBuilder() + .WithTitle("Results!") + .WithDescription("Please select a track:\n") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + + for (var i = 0; i < leng; i++) + { + em.AddField(new($"{i + 1}.{s.ElementAt(i).Info.Title} [{s.ElementAt(i).Info.Length}]", $"by {s.ElementAt(i).Info.Author} [Link]({s.ElementAt(i).Info.Uri})")); + selectOptions.Add(new(s.ElementAt(i).Info.Title, i.ToString(), $"by {s.ElementAt(i).Info.Author}. Length: {s.ElementAt(i).Info.Length}")); + } + + DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); + var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); + return null; + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + var trackSelect = Convert.ToInt32(resp.Result.Values.First()); + var track = s.ElementAt(trackSelect); + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, track); + } + ; + } + } + + public async Task PlaySong() + { + var queue = await Database.GetQueueAsync(this.VoiceChannel.Guild); + var cur = this.LastSong; + if (queue.Count != 1 && this.RepeatMode == RepeatMode.All) + this.RepeatAllPos++; + if (this.RepeatAllPos >= queue.Count) + this.RepeatAllPos = 0; + this.CurrentSong = this.ShuffleMode == ShuffleMode.Off + ? queue[0] + : queue[new Random().Next(0, queue.Count)]; + + switch (this.RepeatMode) + { + case RepeatMode.All: + this.CurrentSong = queue[this.RepeatAllPos]; + break; + case RepeatMode.On: + this.CurrentSong = cur; + break; + } + + MikuBot.ShardedClient.Logger.LogDebug(this.CurrentSong?.Track.Encoded); + this.GuildConnection.TrackEnded += Lavalink.LavalinkTrackFinish; + this.Playstate = Playstate.Playing; + _ = Task.Run(async () => await this.GuildConnection.PlayAsync(this.CurrentSong.Track)); + return this.CurrentSong; + } +} + +// B/S(`・ω・´) ❤️ (´ω`)U/C +*/ + + diff --git a/MikuSharp/Entities/Old/Playlist.cs b/MikuSharp/Entities/Old/Playlist.cs new file mode 100644 index 00000000..11524868 --- /dev/null +++ b/MikuSharp/Entities/Old/Playlist.cs @@ -0,0 +1,70 @@ +/*using DisCatSharp.Lavalink; + +using MikuSharp.Enums; + +using Npgsql; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MikuSharp.Entities; + +public class Playlist +{ + public string Name { get; set; } + public ulong UserID { get; set; } + public ExtService ExternalService { get; set; } + public string Url { get; set; } + public int SongCount { get; set; } + public DateTimeOffset Creation { get; set; } + public DateTimeOffset Modify { get; set; } + + public Playlist(ExtService e, string u, string n, ulong usr, int c, DateTimeOffset crea, DateTimeOffset mody) + { + ExternalService = e; + Url = u; + Name = n; + UserID = usr; + SongCount = c; + Creation = crea; + Modify = mody; + } + + public async Task> GetEntries() + { + var Entries = new List(SongCount); + if (ExternalService == ExtService.None) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlistentries WHERE userid = {UserID} AND playlistname = @pl ORDER BY pos ASC;", conn); + cmd2.Parameters.AddWithValue("pl", Name); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + Entries.Add(new PlaylistEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), DateTimeOffset.Parse(Convert.ToString(reader["addition"])), DateTimeOffset.Parse(Convert.ToString(reader["changed"])), Convert.ToInt32(reader["pos"]))); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } + else + { + var trs = await MikuBot.LavalinkNodeConnections.First().Value.ConnectedGuilds.First().Value.GetTracksAsync(new Uri(Url)); + int i = 0; + foreach (var t in trs.Tracks) + { + Entries.Add(new PlaylistEntry(t, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, i)); + i++; + } + } + return Entries; + } +} +*/ + + diff --git a/MikuSharp/Entities/Old/PlaylistEntry.cs b/MikuSharp/Entities/Old/PlaylistEntry.cs new file mode 100644 index 00000000..d1c9fde1 --- /dev/null +++ b/MikuSharp/Entities/Old/PlaylistEntry.cs @@ -0,0 +1,20 @@ +/*using System; + +using DisCatSharp.Lavalink.Entities; + +namespace MikuSharp.Entities; + +public class PlaylistEntry : Entry +{ + public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset moddate, int pos) : base(t, addDate) + { + this.ModifyDate = moddate; + this.Position = pos; + } + + public DateTimeOffset ModifyDate { get; set; } + public int Position { get; set; } +} +*/ + + diff --git a/MikuSharp/Entities/Old/QueueEntry.cs b/MikuSharp/Entities/Old/QueueEntry.cs new file mode 100644 index 00000000..382774ac --- /dev/null +++ b/MikuSharp/Entities/Old/QueueEntry.cs @@ -0,0 +1,20 @@ +/*using System; + +using DisCatSharp.Lavalink.Entities; + +namespace MikuSharp.Entities; + +public class QueueEntry : Entry +{ + public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : base(t, adddate) + { + this.Position = pos; + this.AddedBy = m; + } + + public int Position { get; set; } + public ulong AddedBy { set; get; } +} +*/ + + diff --git a/MikuSharp/Entities/Old/TrackResult.cs b/MikuSharp/Entities/Old/TrackResult.cs new file mode 100644 index 00000000..5c1399b1 --- /dev/null +++ b/MikuSharp/Entities/Old/TrackResult.cs @@ -0,0 +1,27 @@ +/*using System.Collections.Generic; + +using DisCatSharp.Lavalink.Entities; + +namespace MikuSharp.Entities; + +public class TrackResult +{ + public TrackResult(LavalinkPlaylistInfo pl, IEnumerable tr) + { + this.PlaylistInfo = pl; + this.Tracks = []; + this.Tracks.AddRange(tr); + } + + public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) + { + this.PlaylistInfo = pl; + this.Tracks = [tr]; + } + + public LavalinkPlaylistInfo PlaylistInfo { get; set; } + public List Tracks { get; set; } +} +*/ + + diff --git a/MikuSharp/Entities/Playlist.cs b/MikuSharp/Entities/Playlist.cs deleted file mode 100644 index e9760877..00000000 --- a/MikuSharp/Entities/Playlist.cs +++ /dev/null @@ -1,68 +0,0 @@ -/*using DisCatSharp.Lavalink; - -using MikuSharp.Enums; - -using Npgsql; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Entities; - -public class Playlist -{ - public string Name { get; set; } - public ulong UserID { get; set; } - public ExtService ExternalService { get; set; } - public string Url { get; set; } - public int SongCount { get; set; } - public DateTimeOffset Creation { get; set; } - public DateTimeOffset Modify { get; set; } - - public Playlist(ExtService e, string u, string n, ulong usr, int c, DateTimeOffset crea, DateTimeOffset mody) - { - ExternalService = e; - Url = u; - Name = n; - UserID = usr; - SongCount = c; - Creation = crea; - Modify = mody; - } - - public async Task> GetEntries() - { - var Entries = new List(SongCount); - if (ExternalService == ExtService.None) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlistentries WHERE userid = {UserID} AND playlistname = @pl ORDER BY pos ASC;", conn); - cmd2.Parameters.AddWithValue("pl", Name); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - Entries.Add(new PlaylistEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), DateTimeOffset.Parse(Convert.ToString(reader["addition"])), DateTimeOffset.Parse(Convert.ToString(reader["changed"])), Convert.ToInt32(reader["pos"]))); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - else - { - var trs = await MikuBot.LavalinkNodeConnections.First().Value.ConnectedGuilds.First().Value.GetTracksAsync(new Uri(Url)); - int i = 0; - foreach (var t in trs.Tracks) - { - Entries.Add(new PlaylistEntry(t, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, i)); - i++; - } - } - return Entries; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/PlaylistEntry.cs b/MikuSharp/Entities/PlaylistEntry.cs deleted file mode 100644 index d570f129..00000000 --- a/MikuSharp/Entities/PlaylistEntry.cs +++ /dev/null @@ -1,17 +0,0 @@ -/*using DisCatSharp.Lavalink; - -using System; - -namespace MikuSharp.Entities; - -public class PlaylistEntry : Entry -{ - public DateTimeOffset modifyDate { get; set; } - public int Position { get; set; } - public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset moddate, int pos) : base(t, addDate) - { - modifyDate = moddate; - Position = pos; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/QueueEntry.cs b/MikuSharp/Entities/QueueEntry.cs deleted file mode 100644 index 04069ec1..00000000 --- a/MikuSharp/Entities/QueueEntry.cs +++ /dev/null @@ -1,17 +0,0 @@ -/*using DisCatSharp.Lavalink; - -using System; - -namespace MikuSharp.Entities; - -public class QueueEntry : Entry -{ - public int position { get; set; } - public ulong addedBy { set; get; } - public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : base(t, adddate) - { - position = pos; - addedBy = m; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index 3a7d24a9..d32ce98d 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -1,7 +1,10 @@ -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; -public class Random_D +public sealed class RandomD : ImgData { - public string url { get; set; } - public string message { get; set; } + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } } diff --git a/MikuSharp/Entities/TrackResult.cs b/MikuSharp/Entities/TrackResult.cs deleted file mode 100644 index 557a7279..00000000 --- a/MikuSharp/Entities/TrackResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -/*using DisCatSharp.Lavalink; - -using System.Collections.Generic; - -namespace MikuSharp.Entities; - -public class TrackResult -{ - public LavalinkPlaylistInfo PlaylistInfo { get; set; } - public List Tracks { get; set; } - public TrackResult(LavalinkPlaylistInfo pl, IEnumerable tr) - { - PlaylistInfo = pl; - Tracks = new List(); - Tracks.AddRange(tr); - } - public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) - { - PlaylistInfo = pl; - Tracks = new List - { - tr - }; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index 054a4c59..4f0f9081 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -1,10 +1,6 @@ -using DisCatSharp.Entities; +namespace MikuSharp.Entities; -using System.IO; - -namespace MikuSharp.Entities; - -public class WeebSh +public sealed class WeebSh { public MemoryStream ImgData { get; set; } public string Extension { get; set; } diff --git a/MikuSharp/Enums/ExtService.cs b/MikuSharp/Enums/ExtService.cs index edb4309c..b3819ef4 100644 --- a/MikuSharp/Enums/ExtService.cs +++ b/MikuSharp/Enums/ExtService.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Enums; -public enum ExtService : int +public enum ExtService { None = 0, Youtube = 1, diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index 77fc0e19..1ca45bc3 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -1,20 +1,8 @@ -namespace MikuSharp.Enums; +namespace MikuSharp.Enums; -public enum Playstate : int +public enum PlaybackState { - NotPlaying = 0, + Stopped = 0, Playing = 1, - Paused = 2, - Stopped = 3 -} -public enum RepeatMode : int -{ - Off = 0, - On = 1, - All = 2 -} -public enum ShuffleMode : int -{ - Off = 0, - On = 1 + Paused = 2 } diff --git a/MikuSharp/Events/Lavalink.cs b/MikuSharp/Events/Lavalink.cs deleted file mode 100644 index 91ec1069..00000000 --- a/MikuSharp/Events/Lavalink.cs +++ /dev/null @@ -1,90 +0,0 @@ -/*using DisCatSharp.Entities; -using DisCatSharp.Lavalink; -using DisCatSharp.Lavalink.EventArgs; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Enums; -using MikuSharp.Utilities; - -using System; -using System.Threading.Tasks; - -namespace MikuSharp.Events; - -public class Lavalink -{ - public static async Task LavalinkTrackFinish(LavalinkGuildConnection lava, TrackFinishEventArgs e) - { - try - { - var g = MikuBot.Guilds[e.Player.Guild.Id]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Player.Guild); - if (g.musicInstance == null) - return; - switch (e.Reason) - { - case TrackEndReason.Stopped: - { - g.musicInstance.playstate = Playstate.Stopped; - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - break; - } - case TrackEndReason.Replaced: - { - break; - } - case TrackEndReason.LoadFailed: - { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithTitle("Track failed to play") - .WithDescription($"**{g.musicInstance.currentSong.track.Title}**\nby {g.musicInstance.currentSong.track.Author}\n" + - $"**Failed to load, Skipping to next track**")); - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, e.Player.Guild); - if (lastPlayedSongs.Count == 0) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - else if (lastPlayedSongs[0].track.Uri != g.musicInstance.currentSong.track.Uri) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - var queue = await Database.GetQueueAsync(e.Player.Guild); - if (queue.Count != 0) await g.musicInstance.PlaySong(); - else g.musicInstance.playstate = Playstate.NotPlaying; - break; - } - case TrackEndReason.Finished: - { - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - if (g.musicInstance.repeatMode != RepeatMode.On && g.musicInstance.repeatMode != RepeatMode.All) await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, e.Player.Guild); - if (lastPlayedSongs.Count == 0) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - else if (lastPlayedSongs[0].track.Uri != g.musicInstance.currentSong.track.Uri) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - var queue = await Database.GetQueueAsync(e.Player.Guild); - if (queue.Count != 0) await g.musicInstance.PlaySong(); - else g.musicInstance.playstate = Playstate.NotPlaying; - break; - } - } - } - catch (Exception ex) - { - MikuBot.ShardedClient.Logger.LogError(ex.Message); - MikuBot.ShardedClient.Logger.LogError(ex.StackTrace); - } - } - -} -*/ \ No newline at end of file diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index 6b67b421..fbdb7220 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -1,41 +1,26 @@ -using DisCatSharp; using DisCatSharp.EventArgs; -using System.Threading.Tasks; - namespace MikuSharp.Events; /// -/// Event handler for the miku guild. +/// Event handler for the miku guild. /// public class MikuGuild { /// - /// Fired when a new guild member joins. - /// - /// The client. - /// The event args. - public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) - { - await Task.FromResult(true); - } - - /// - /// Fired when a guild member is updated. + /// Fired when a guild member is updated. /// /// The discord client. /// The event args. public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) { - if (args.PendingBefore.HasValue && args.PendingBefore == true) + if (args is { PendingBefore: true, PendingAfter: false }) { - if (args.PendingAfter.HasValue && args.PendingAfter == false) - { - ulong member_role_id = 483280207927574528; - var member_role = args.Guild.GetRole(member_role_id); - await args.Member.GrantRoleAsync(member_role); - } + ulong memberRoleId = 483280207927574528; + var memberRole = await args.Guild.GetRoleAsync(memberRoleId); + await args.Member.GrantRoleAsync(memberRole); } + await Task.FromResult(true); } } diff --git a/MikuSharp/Events/Old/VoiceChat.cs b/MikuSharp/Events/Old/VoiceChat.cs new file mode 100644 index 00000000..7fc59b75 --- /dev/null +++ b/MikuSharp/Events/Old/VoiceChat.cs @@ -0,0 +1,71 @@ +/*using System; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp; +using DisCatSharp.Entities; +using DisCatSharp.EventArgs; + +using Microsoft.Extensions.Logging; + +using MikuSharp.Enums; + +namespace MikuSharp.Events; + +public class VoiceChat +{ + public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventArgs e) + { + try + { + if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) + return; + + var g = MikuBot.Guilds[e.Guild.Id]; + if (g.MusicInstance == null || g.MusicInstance?.GuildConnection?.IsConnected == false) return; + + if ((e.After?.Channel?.Users?.Count(x => !x.IsBot) == 0 || e.Before?.Channel?.Users?.Count(x => !x.IsBot) == 0 || e.Channel?.Users?.Count(x => !x.IsBot) == 0) && + (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || + e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || + e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) && + g.MusicInstance?.GuildConnection?.Channel?.Users?.Count(x => !x.IsBot) == 0) + { + if (g.MusicInstance.Playstate == Playstate.Playing) + { + await g.MusicInstance.GuildConnection.PauseAsync(); + g.MusicInstance.Playstate = Playstate.Paused; + + try + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder() + .WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); + } + catch + { } + } + else + try + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); + } + catch + { } + + g.MusicInstance.AloneTime = DateTime.UtcNow; + g.MusicInstance.AloneCts = new(); + g.AloneCheckThread = Task.Run(g.CheckAlone); + } + else if (e.After?.Channel?.Users?.Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) + if (g.MusicInstance is { AloneCts: not null }) + await g.MusicInstance.AloneCts.CancelAsync(); + } + catch (Exception ex) + { + client.Logger.LogError(ex.Message); + client.Logger.LogError(ex.StackTrace); + } + } +} +*/ + + diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/VoiceChat.cs deleted file mode 100644 index 8740d8f1..00000000 --- a/MikuSharp/Events/VoiceChat.cs +++ /dev/null @@ -1,71 +0,0 @@ -/*using DisCatSharp; -using DisCatSharp.Entities; -using DisCatSharp.EventArgs; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Enums; - -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MikuSharp.Events; - -public class VoiceChat -{ - public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventArgs e) - { - try - { - if (!MikuBot.Guilds.Any(x => x.Key == e.Guild.Id)) return; - var g = MikuBot.Guilds[e.Guild.Id]; - if (g.musicInstance == null - || g.musicInstance?.guildConnection?.IsConnected == false) return; - if ((e.After?.Channel?.Users.Where(x => !x.IsBot).Count() == 0 - || e.Before?.Channel?.Users.Where(x => !x.IsBot).Count() == 0 - || e.Channel?.Users.Where(x => !x.IsBot).Count() == 0) - && (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - && g.musicInstance?.guildConnection?.Channel?.Users.Where(x => !x.IsBot).Count() == 0) - { - if (g.musicInstance.playstate == Playstate.Playing) - { - await g.musicInstance.guildConnection.PauseAsync(); - g.musicInstance.playstate = Playstate.Paused; - try - { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); - } - catch { } - } - else - { - try - { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); - } - catch { } - } - g.musicInstance.aloneTime = DateTime.UtcNow; - g.musicInstance.aloneCTS = new CancellationTokenSource(); - g.AloneCheckThread = Task.Run(g.CheckAlone); - } - else if (e.After?.Channel?.Users.Where(x => !x.IsBot).Count() != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - { - if (g.musicInstance != null && g.musicInstance?.aloneCTS != null) - { - g.musicInstance.aloneCTS.Cancel(); - } - } - } - catch (Exception ex) - { - client.Logger.LogError(ex.Message); - client.Logger.LogError(ex.StackTrace); - } - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index 44e3a523..19415cd5 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. @@ -86,59 +86,161 @@ [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.QueueEntry.position")] [assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.Dispose")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot._cts")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.GameSetThread")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.StatusThread")] @@ -252,14 +354,14 @@ [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.WeebSh.Extension")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.WeebSh.ImgData")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.MikuBot")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Action")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Fun")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.About")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Moderation")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.ActionCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.FunCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.AboutCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuildCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.ModerationCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Music")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.NSFW")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Utility")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.UtilityCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Weeb")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.BiliJson")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.BiliPlayinfo")] @@ -297,19 +399,27 @@ [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.NND")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Other")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.PlaylistDB")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Web")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.WebExtensionMethods")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuildCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.Random_D")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] @@ -320,11 +430,24 @@ [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.RegisterEvents~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] -[assembly: SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.WebExtensionMethods.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] +[assembly: + SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] diff --git a/MikuSharp/GlobalUsings.cs b/MikuSharp/GlobalUsings.cs new file mode 100644 index 00000000..194523a7 --- /dev/null +++ b/MikuSharp/GlobalUsings.cs @@ -0,0 +1,31 @@ +global using System; +global using System.Collections.Concurrent; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Threading; +global using System.Threading.Tasks; + +global using DisCatSharp; +global using DisCatSharp.ApplicationCommands; +global using DisCatSharp.ApplicationCommands.Attributes; +global using DisCatSharp.ApplicationCommands.Context; +global using DisCatSharp.CommandsNext; +global using DisCatSharp.CommandsNext.Attributes; +global using DisCatSharp.Entities; +global using DisCatSharp.Enums; +global using DisCatSharp.Interactivity; +global using DisCatSharp.Interactivity.Enums; +global using DisCatSharp.Interactivity.Extensions; +global using DisCatSharp.Lavalink; +global using DisCatSharp.Lavalink.Entities; +global using DisCatSharp.Lavalink.Enums; + +global using Microsoft.Extensions.Logging; + +global using NeoSmart.AsyncLock; + +global using Newtonsoft.Json; + +global using Serilog; diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs new file mode 100644 index 00000000..dc88f7d2 --- /dev/null +++ b/MikuSharp/HatsuneMikuBot.cs @@ -0,0 +1,408 @@ +using System.Net; + +using DisCatSharp.ApplicationCommands.Exceptions; + +using DiscordBotsList.Api; + +using MikuSharp.Attributes; +using MikuSharp.Commands; +using MikuSharp.Commands.Music; +using MikuSharp.Commands.Playlist; +using MikuSharp.Entities; +using MikuSharp.Events; + +using Serilog.Events; + +using Weeb.net; + +using TokenType = DisCatSharp.Enums.TokenType; + +namespace MikuSharp; + +/// +/// The Hatsune Miku bot. +/// +public sealed class HatsuneMikuBot : IDisposable +{ + /// + /// Whether to enable the proxy. + /// + public const bool ENABLE_PROXY = false; + + /// + /// Whether to disable the gateway compression. + /// + public const bool DISABLE_GATEWAY_COMPRESSION = false; + + /// + /// Whether to disable Lavalink. + /// + public const bool DISABLE_LAVALINK = false; + + /// + /// Gets the Weeb client. + /// + internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "5.0.0"); + + /// + /// Gets the music sessions. + /// + internal static readonly ConcurrentDictionary MusicSessions = []; + + /// + /// Gets the music session locks. + /// + internal static readonly ConcurrentDictionary MusicSessionLocks = []; + + /// + /// Initializes a new instance of the class. + /// + /// Thrown when the config file is null or missing. + public HatsuneMikuBot() + { + var fileData = File.ReadAllText("config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); + + var config = JsonConvert.DeserializeObject(fileData); + ArgumentNullException.ThrowIfNull(config); + Config = config; + + Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) + .WriteTo.Console(LogEventLevel.Debug) + .CreateLogger(); + Log.Logger.Information("Starting up!"); + + ShardedClient = new(new() + { + Token = Config.DiscordToken, + TokenType = TokenType.Bot, + MinimumLogLevel = LogLevel.Debug, + AutoReconnect = true, + ApiChannel = ApiChannel.Canary, + HttpTimeout = TimeSpan.FromMinutes(1), + Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, + MessageCacheSize = 2048, + LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), + ShowReleaseNotesInUpdateCheck = false, + IncludePrereleaseInUpdateCheck = true, + DisableUpdateCheck = true, + EnableSentry = true, + FeedbackEmail = "aiko@aitsys.dev", + DeveloperUserId = 856780995629154305, + AttachUserInfo = true, + ReconnectIndefinitely = true, + EnableLibraryDeveloperMode = true, + Proxy = ENABLE_PROXY ? new WebProxy("127.0.0.1", 8000) : null, + GatewayCompressionLevel = DISABLE_GATEWAY_COMPRESSION ? GatewayCompressionLevel.None : GatewayCompressionLevel.Stream + }); + + this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() + { + Timeout = TimeSpan.FromMinutes(2), + PaginationBehaviour = PaginationBehaviour.WrapAround, + PaginationDeletion = PaginationDeletion.DeleteEmojis, + PollBehaviour = PollBehaviour.DeleteEmojis, + AckPaginationButtons = true, + ButtonBehavior = ButtonPaginationBehavior.Disable, + PaginationButtons = new() + { + SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), + Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), + Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), + Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), + SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) + }, + ResponseMessage = "Something went wrong.", + ResponseBehavior = InteractionResponseBehavior.Ignore + }).Result; + + this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() + { + EnableDefaultHelp = true, + DebugStartup = false, + EnableLocalization = false, + GenerateTranslationFilesOnly = false + }).Result; + + this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() + { + CaseSensitive = false, + EnableMentionPrefix = true, + DmHelp = false, + EnableDefaultHelp = true, + IgnoreExtraArguments = true, + StringPrefixes = [], + UseDefaultCommandHandler = true, + DefaultHelpChecks = [new NotDiscordStaffAttribute()] + }).Result; + + this.LavalinkConfig = new() + { + SocketEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + RestEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + Password = Config.LavaConfig.Password, + EnableBuiltInQueueSystem = true, + QueueEntryFactory = () => new MusicQueueEntry() + }; + + this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; + } + + /// + /// Gets the cancellation token source. + /// + internal static CancellationTokenSource MikuCancellationTokenSource { get; set; } = new(); + + /// + /// Gets the bot configuration. + /// + internal static BotConfig Config { get; set; } + + /// + /// Gets the Lavalink configuration. + /// + internal LavalinkConfiguration LavalinkConfig { get; set; } + + /// + /// Gets or sets the game set thread. + /// + internal Task? GameSetThread { get; set; } + + /// + /// Gets or sets the bot list thread. + /// + internal Task? BotListThread { get; set; } + + /// + /// Gets the Discord Bot List API. + /// + internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } + + /// + /// Gets the sharded client. + /// + internal static DiscordShardedClient ShardedClient { get; set; } + + /// + /// Gets the interactivity modules. + /// + internal IReadOnlyDictionary InteractivityModules { get; set; } + + /// + /// Gets the application commands modules. + /// + internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } + + /// + /// Gets the commands next modules. + /// + internal IReadOnlyDictionary CommandsNextModules { get; set; } + + /// + /// Gets the Lavalink modules. + /// + internal IReadOnlyDictionary LavalinkModules { get; set; } + + /// + public void Dispose() + { + GC.SuppressFinalize(this); + ShardedClient = null!; + } + + /// + /// Registers the events. + /// + internal static async Task RegisterEventsAsync() + { + ShardedClient.ClientErrored += (sender, args) => + { + sender.Logger.LogError(args.Exception, "{Message}", args.Exception.Message); + sender.Logger.LogError(args.Exception, "{Stack}", args.Exception.StackTrace); + return Task.CompletedTask; + }; + + await Task.Delay(1); + + foreach (var discordClientKvp in ShardedClient.ShardClients) + { + //discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => + { + sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => + { + sender.Client.Logger.LogInformation("Application commands module is ready"); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GetApplicationCommands().SlashCommandErrored += async (sender, args) => + { + if (args.Exception is SlashExecutionChecksFailedException ex) + if (ex.FailedChecks.Any(x => x is ApplicationCommandRequireTeamMemberAttribute)) + { + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("This command is limit to developers")); + return; + } + + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("An error occurred while executing this command.")); + }; + + discordClientKvp.Value.GetApplicationCommands().ContextMenuErrored += async (sender, args) => + { + if (args.Exception is SlashExecutionChecksFailedException ex) + if (ex.FailedChecks.Any(x => x is ApplicationCommandRequireTeamMemberAttribute)) + { + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("This command is limit to developers")); + return; + } + + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("An error occurred while executing this command.")); + }; + + discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => + { + if (args.Guild.Id == 483279257431441410) + await MikuGuild.OnUpdateAsync(sender, args); + else + await Task.FromResult(true); + }; + + discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); + } + } + + /// + /// Updates the bot list statistics. + /// + internal static async Task UpdateBotListStatisticsAsync() + { + await Task.Delay(15000); + + while (true) + { + var me = await DiscordBotListApi.GetMeAsync(); + var count = Array.Empty(); + var clients = ShardedClient.ShardClients.Values; + count = clients.Aggregate(count, (current, client) => [.. current, client.Guilds.Count]); + await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); + await Task.Delay(TimeSpan.FromMinutes(15)); + } + } + + /// + /// Rotates the activity every 20 minutes. + /// + internal static async Task RotateActivityAsync() + { + while (true) + { + DiscordActivity firstActivity = new() + { + Name = "New music system coming up soon!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(firstActivity, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity secondActivity = new() + { + Name = "Mention me with help for other commands!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(secondActivity, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity thirdActivity = new() + { + Name = "Full NND support!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(thirdActivity, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + } + } + + /// + /// Registers the commands. + /// + internal void RegisterCommands() + { + // Nsfw stuff needs to be hidden, that's why we use commands next + this.CommandsNextModules.RegisterCommands(); + + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + + // Smolcar command, miku discord guild command + this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); + } + + /// + /// Runs the bot. + /// + internal async Task RunAsync() + { + await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); + await ShardedClient.StartAsync(); + await Task.Delay(5000); + + if (!DISABLE_LAVALINK) + { + var success = false; + while (!success) + try + { + foreach (var lavalinkShard in this.LavalinkModules) + await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + success = true; + } + catch + { + success = false; + } + } + + foreach (var client in ShardedClient.ShardClients.Values) + await client.GetApplicationEmojisAsync(true); + + DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); + this.GameSetThread = Task.Run(RotateActivityAsync); +#if !DEBUG + this.BotListThread = Task.Run(UpdateBotListStatisticsAsync); +#endif + while (!MikuCancellationTokenSource.IsCancellationRequested) + await Task.Delay(25); + _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); + await ShardedClient.StopAsync(); + } + + /// + ~HatsuneMikuBot() + { + this.Dispose(); + } +} diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs deleted file mode 100644 index 22215f4a..00000000 --- a/MikuSharp/MikuBot.cs +++ /dev/null @@ -1,304 +0,0 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Enums; -using DisCatSharp.Interactivity.EventHandling; -using DisCatSharp.Interactivity.Extensions; -//using DisCatSharp.Lavalink; -using DisCatSharp.Net; - -using DiscordBotsList.Api; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Entities; -using MikuSharp.Enums; -using MikuSharp.Events; - -using Newtonsoft.Json; - -using Serilog; -using Serilog.Events; - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using Weeb.net; - -namespace MikuSharp; - -internal class MikuBot : IDisposable -{ - internal static CancellationTokenSource _cts { get; set; } - - internal static BotConfig Config { get; set; } - //internal LavalinkConfiguration LavalinkConfig { get; set; } - - internal Task GameSetThread { get; set; } - internal Task StatusThread { get; set; } - internal Task BotListThread { get; set; } - - internal static WeebClient _weebClient = new("Hatsune Miku Bot", "4.0.0"); - internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } - internal static DiscordShardedClient ShardedClient { get; set; } - - internal IReadOnlyDictionary InteractivityModules { get; set; } - internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } - internal IReadOnlyDictionary CommandsNextModules { get; set; } - //internal IReadOnlyDictionary LavalinkModules { get; set; } - - //internal static Dictionary LavalinkNodeConnections = new(); - //internal static Dictionary Guilds = new(); - - internal static Playstate ps = Playstate.Playing; - internal static Stopwatch psc = new(); - - - internal MikuBot() - { - var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException("config.json is null or missing"); - - Config = JsonConvert.DeserializeObject(fileData); - if (Config == null) - throw new ArgumentNullException("config.json is null"); - - Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - _cts = new CancellationTokenSource(); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) - .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug, outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") - .CreateLogger(); - Log.Logger.Information("Starting up!"); - - ShardedClient = new DiscordShardedClient(new() - { - Token = Config.DiscordToken, - TokenType = DisCatSharp.Enums.TokenType.Bot, - MinimumLogLevel = LogLevel.Debug, - AutoReconnect = true, - ApiChannel = ApiChannel.Canary, - HttpTimeout = TimeSpan.FromMinutes(1), - Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, - MessageCacheSize = 2048, - LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), - ShowReleaseNotesInUpdateCheck = false, - IncludePrereleaseInUpdateCheck = true, - DisableUpdateCheck = true, - EnableSentry = true, - FeedbackEmail = "aiko@aitsys.dev", - DeveloperUserId = 856780995629154305, - AttachUserInfo = true, - ReconnectIndefinitely = true - }); - - InteractivityModules = ShardedClient.UseInteractivityAsync(new() - { - Timeout = TimeSpan.FromMinutes(2), - PaginationBehaviour = PaginationBehaviour.WrapAround, - PaginationDeletion = PaginationDeletion.DeleteEmojis, - PollBehaviour = PollBehaviour.DeleteEmojis, - AckPaginationButtons = true, - ButtonBehavior = ButtonPaginationBehavior.Disable, - PaginationButtons = new PaginationButtons() - { - SkipLeft = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-skip-left", "First", false, new DiscordComponentEmoji("⏮️")), - Left = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-left", "Previous", false, new DiscordComponentEmoji("◀️")), - Stop = new DiscordButtonComponent(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new DiscordComponentEmoji("⏹️")), - Right = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-right", "Next", false, new DiscordComponentEmoji("▶️")), - SkipRight = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new DiscordComponentEmoji("⏭️")) - }, - ResponseMessage = "Something went wrong.", - ResponseBehavior = InteractionResponseBehavior.Ignore - }).Result; - - ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() - { - EnableDefaultHelp = true, - DebugStartup = true, - EnableLocalization = false, - ManualOverride = true, - GenerateTranslationFilesOnly = false - }).Result; - - CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() - { - CaseSensitive = true, - EnableMentionPrefix = true, - DmHelp = false, - EnableDefaultHelp = true, - IgnoreExtraArguments = true, - StringPrefixes = new List(), - UseDefaultCommandHandler = true, - DefaultHelpChecks = new List(1) { new Attributes.NotStaffAttribute() } - }).Result; - - /*LavalinkConfig = new() - { - SocketEndpoint = new ConnectionEndpoint { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, - RestEndpoint = new ConnectionEndpoint { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, - Password = Config.LavaConfig.Password - }; - - LavalinkModules = ShardedClient.UseLavalinkAsync().Result;*/ - } - - internal static async Task RegisterEvents() - { - ShardedClient.ClientErrored += (sender, args) => - { - sender.Logger.LogError(args.Exception.Message); - sender.Logger.LogError(args.Exception.StackTrace); - return Task.CompletedTask; - }; - - await Task.Delay(1); - - foreach (var discordClientKvp in ShardedClient.ShardClients) - { - //discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; - - discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => - { - sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", sender.Client.ShardId); - return Task.CompletedTask; - }; - - discordClientKvp.Value.GuildMemberAdded += async (sender, args) => - { - if (sender.CurrentApplication.Team.Members.Where(x => x.User.Id == args.Member.Id).Any()) - { - var text = $"Heywo <:MikuWave:655783221942026271>!" + - $"\n\nOne of my developers joined your server!" + - $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + - $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + - $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + - $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithDiscriminator}!" + - $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; - var message = await args.Guild.Owner.SendMessageAsync(text); - sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithDiscriminator); - sender.Logger.LogInformation("Message content: {content}", message.Content); - } - else - await Task.FromResult(true); - }; - discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => - { - if (args.Guild.Id == 483279257431441410) - await MikuGuild.OnUpdateAsync(sender, args); - else - await Task.FromResult(true); - }; - - discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); - } - } -/* - internal async Task ShowConnections() - { - while (true) - { - var al = Guilds.Where(x => x.Value?.musicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Where(x => x.Value.musicInstance.guildConnection?.IsConnected == true).Count()); - await Task.Delay(15000); - } - } -*/ - internal static async Task UpdateBotList() - { - await Task.Delay(15000); - while (true) - { - var me = await DiscordBotListApi.GetMeAsync(); - int[] count = Array.Empty(); - var clients = ShardedClient.ShardClients.Values; - foreach (var client in clients) - count = count.Append(client.Guilds.Count).ToArray(); - await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); - await Task.Delay(TimeSpan.FromMinutes(15)); - } - } - - internal async Task SetActivity() - { - while (true) - { - DiscordActivity test = new() - { - Name = "I'm using slash commands now!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(activity: test, userStatus: UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test2 = new() - { - Name = "Mention me with help for nsfw commands!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(activity: test2, userStatus: UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test3 = new() - { - Name = "Full NND support!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(activity: test3, userStatus: UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - } - } - - internal void RegisterCommands() - { - // Nsfw stuff needs to be hidden, that's why we use commands next - CommandsNextModules.RegisterCommands(); - - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - - // Smolcar command, only guild command - ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); - } - - internal async Task RunAsync() - { - await _weebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); - await ShardedClient.StartAsync(); - await Task.Delay(5000); - /*foreach (var lavalinkShard in LavalinkModules) - { - var LCon = await lavalinkShard.Value.ConnectAsync(LavalinkConfig); - LavalinkNodeConnections.Add(lavalinkShard.Key, LCon); - }*/ - GameSetThread = Task.Run(SetActivity); - //StatusThread = Task.Run(ShowConnections); - //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); - //BotListThread = Task.Run(UpdateBotList); - while (!_cts.IsCancellationRequested) - { - await Task.Delay(25); - } - await ShardedClient.StopAsync(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - } -} diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index fcc476dd..585e09f4 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -1,133 +1,145 @@  - - Exe - net7.0 - MikuSharp.Program - miku.ico - 4.0.0 - MikuSharp Team - https://github.com/Sekoree/MikuSharp/ - git - https://github.com/Sekoree/MikuSharp/ - MIT - Full Hatsune Miku Discord bot C# Rewrite! - enable - Hatsune Miku Discord MikuBot - MikuSharp Team - miku.jpg - README.md - discord bot; discatsharp; hatsune miku; miku; bot - - - - - - - - + + Exe + net9.0 + MikuSharp.Program + miku.ico + 5.0.0 + aiko, MikuSharp Team + https://github.com/Sekoree/MikuSharp/ + git + https://github.com/Sekoree/MikuSharp/ + MIT + The One And Only Hatsune Miku Bot! + enable + Hatsune Miku Discord Bot + MikuSharp Team + miku.png + README.md + discord bot; discatsharp; hatsune miku; miku; bot + true + false + + + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 - + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 - + - - True - \ - + + True + \ + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - True - \ - - - Always - - - Always - - - Always - - - Always - - - Always - - + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + True + \ + + + Always + + + Always + + + Always + + + Always + + + Always + + - + \ No newline at end of file diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 885998a9..06621575 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -1,18 +1,25 @@ -using Serilog; +namespace MikuSharp; -namespace MikuSharp; - -class Program +/// +/// The main program class. +/// +public class Program { - static void Main(string[] args) + /// + /// The main entry point of the application. + /// + /// The optional command-line arguments. + public static void Main(string[]? args = null) { - using (var bot = new MikuBot()) + Log.Logger.Information("Startup!"); + + using (var bot = new HatsuneMikuBot()) { - MikuBot.RegisterEvents().Wait(); + HatsuneMikuBot.RegisterEventsAsync().Wait(); bot.RegisterCommands(); bot.RunAsync().Wait(); - bot.Dispose(); } + Log.Logger.Information("Shutdown!"); } } diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index cc048e67..eb177bae 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -1,23 +1,14 @@ -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using Microsoft.Extensions.Logging; - -using NYoutubeDL; - -using System; -using System.IO; -using System.Threading.Tasks; +using NYoutubeDL; namespace MikuSharp.Utilities; public static class Bilibili { - public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msg_id) + public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) { try { - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; @@ -25,17 +16,12 @@ public static async Task GetBilibiliAsync(this InteractionContext youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; youtubeDl.Options.PostProcessingOptions.AddMetadata = true; youtubeDl.Options.PostProcessingOptions.KeepVideo = false; - youtubeDl.StandardOutputEvent += (e, f) => - { - ctx.Client.Logger.LogDebug("{data}", f); - }; - youtubeDl.StandardErrorEvent += (e, f) => - { - ctx.Client.Logger.LogDebug("{data}", f); - }; + youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; await youtubeDl.DownloadAsync(); var ms = new MemoryStream(); + if (File.Exists($@"{s}.mp3")) { var song = File.Open($@"{s}.mp3", FileMode.Open); @@ -44,6 +30,7 @@ public static async Task GetBilibiliAsync(this InteractionContext song.Close(); File.Delete($@"{s}.mp3"); } + return ms; } catch (Exception ex) diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Database.cs deleted file mode 100644 index ff0e53ee..00000000 --- a/MikuSharp/Utilities/Database.cs +++ /dev/null @@ -1,335 +0,0 @@ -/*using DisCatSharp.Entities; -using DisCatSharp.Lavalink; - -using MikuSharp.Entities; - -using Npgsql; - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace MikuSharp.Utilities; - -public class Database -{ - public static async Task AddToLastPlayingListAsync(ulong g, string ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guildId = {g};", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "ts"; - para2.Value = ts; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "pos"; - para3.Value = position; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task ReorderQueue(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = await GetQueueAsync(g); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - int i = 0; - string longcmd = ""; - foreach (var qi in queueNow) - { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.addedBy}','{qi.track.TrackString}',@adddate{i});"; - i++; - } - var cmd2 = new NpgsqlCommand(); - if (string.IsNullOrEmpty(longcmd)) - return; - cmd2.CommandText = longcmd; - cmd2.Connection = conn; - var para = cmd2.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); - cmd2.Parameters.Add(para); - i = 0; - foreach(var qi in queueNow) - { - var para2 = cmd2.CreateParameter(); - para2.ParameterName = $"adddate{i}"; - para2.Value = qi.additionDate.UtcDateTime; - cmd2.Parameters.Add(para2); - i++; - } - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task RebuildQueue(DiscordGuild g, List q) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = q; - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - int i = 0; - string longcmd = ""; - foreach (var qi in queueNow) - { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.addedBy}','{qi.track.TrackString}',@adddate{i});"; - i++; - } - var cmd2 = new NpgsqlCommand(); - if (string.IsNullOrEmpty(longcmd)) - return; - cmd2.CommandText = longcmd; - cmd2.Connection = conn; - var para = cmd2.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); - cmd2.Parameters.Add(para); - i = 0; - foreach (var qi in queueNow) - { - var para2 = cmd2.CreateParameter(); - para2.ParameterName = $"adddate{i}"; - para2.Value = qi.additionDate.UtcDateTime; - cmd2.Parameters.Add(para2); - i++; - } - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task> GetQueueAsync(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guildId = {g.Id} ORDER BY position ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List queue = new(); - while (await reader.ReadAsync()) - { - queue.Add(new QueueEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), Convert.ToUInt64(reader["userid"]), DateTimeOffset.Parse(reader["addtime"].ToString()), Convert.ToInt32(reader["position"]))); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return queue; - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@pos,@guild,@user,@ts,@time)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "ts"; - para3.Value = ts; - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "pos"; - para4.Value = position; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "time"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - string longcmd = ""; - foreach (var tt in ts) - { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.TrackString}',@time);"; - position++; - } - var cmd = new NpgsqlCommand(longcmd, conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "time"; - para3.Value = DateTime.UtcNow; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - string longcmd = ""; - foreach (var tt in ts) - { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.track.TrackString}',@time);"; - position++; - } - var cmd = new NpgsqlCommand(longcmd, conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "time"; - para3.Value = DateTime.UtcNow; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) - { - var qnow = await GetQueueAsync(g); - qnow.Insert(pos, new QueueEntry(LavalinkUtilities.DecodeTrack(ts), u, DateTimeOffset.UtcNow, pos)); - await RebuildQueue(g, qnow); - } - - public static async Task InsertToQueue(DiscordGuild g, ulong u, List ts, int pos) - { - var qnow = await GetQueueAsync(g); - foreach (var tt in ts) - { - qnow.Insert(pos, new QueueEntry(LavalinkUtilities.DecodeTrack(tt.TrackString), u, DateTimeOffset.UtcNow, pos)); - } - await RebuildQueue(g, qnow); - } - - public static async Task RemoveFromQueueAsync(int position, DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guildid = {g.Id};", conn); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - await ReorderQueue(g); - conn.Close(); - conn.Dispose(); - } - - public static async Task ClearQueue(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task MoveQueueItems(DiscordGuild g, int oldpos, int newpos) - { - var qnow = await GetQueueAsync(g); - var temp = qnow[oldpos]; - qnow.RemoveAt(oldpos); - qnow.Insert(newpos, temp); - await RebuildQueue(g, qnow); - } - - public static async Task> GetLastPlayingListAsync(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guildId = {g.Id} ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List queue = new(); - while (await reader.ReadAsync()) - queue.Add(new Entry(LavalinkUtilities.DecodeTrack((string)reader["trackstring"]), DateTimeOffset.UtcNow)); - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return queue; - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs new file mode 100644 index 00000000..62de0e80 --- /dev/null +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -0,0 +1,192 @@ +using System.Diagnostics.CodeAnalysis; + +using HeyRed.Mime; + +using MikuSharp.Entities; + +using Weeb.net.Data; + +namespace MikuSharp.Utilities; + +/// +/// Contains extension methods for Discord-related classes. +/// +public static class DiscordExtensionMethods +{ + /// + /// Gets the invisible space string. + /// + private const string INVISIBLE_CHARACTER = "\u200e"; + + /// + /// Gets an empty component using the \u200e annotation. + /// + public static DiscordTextDisplayComponent EmptyComponent { get; } = new(INVISIBLE_CHARACTER); + + /// + /// Gets the avatar URL of the user, using the guild avatar URL if possible. + /// + /// The context. + /// The user. + /// The avatar URL. + public static string GetGuildAvatarIfPossible(this BaseContext ctx, DiscordUser user) + => ctx.Guild is not null && ctx.Guild.TryGetMember(user.Id, out var member) + ? member.GuildAvatarUrl + : user.AvatarUrl; + + /// + /// Gets the display name of the user, using the guild display name if possible. + /// + /// The context. + /// The user. + /// The display name. + public static string GetGuildOrGlobalDisplayNameIfPossible(this BaseContext ctx, DiscordUser user) + => ctx.Guild is not null && ctx.Guild.TryGetMember(user.Id, out var member) + ? member.DisplayName + : user.GetGlobalOrUsername(); + + /// + /// Gets the global name of the user, using the username if the global name is not set. + /// + /// The user. + /// The global name or username. + public static string GetGlobalOrUsername(this DiscordUser user) + => user.GlobalName ?? user.Username; + + /// + /// Sends an Action message. + /// + /// The context. + /// The image. + /// The optional title. + /// The optional content. + /// The additional user to allow to be pinged (author is already added). + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// + public static async Task SendActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null, string? footer = null) + { + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); + DiscordContainerComponent container = new(); + if (title is not null) + container.AddComponent(new DiscordTextDisplayComponent(title)); + if (content is not null) + container.AddComponent(new DiscordTextDisplayComponent(content)); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])); + if (footer is not null) + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); + builder.AddComponents(container); + builder.WithAllowedMention(new UserMention(context.User)); + if (user is not null) + builder.WithAllowedMention(new UserMention(user)); + await context.EditResponseAsync(builder); + await image.DisposeAsync(); + } + + /// + /// Sends a Weeb message. + /// + /// The context. + /// The image. + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// + public static async Task SendWeebMessageAsync(this BaseContext context, MeekMoeImage image, string? footer = null) + { + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + builder.AddFile($"image.{image.FileType}", image.Data); + DiscordContainerComponent container = new(); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{image.FileType}")])); + if (footer is not null) + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); + builder.AddComponents(container); + await context.EditResponseAsync(builder); + await image.Data.DisposeAsync(); + } + + /// + /// Sends an image message. + /// + /// The context. + /// The image. + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// + public static async Task SendImageMessageAsync(this BaseContext context, MemoryStream image, string? footer = null) + { + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); + DiscordContainerComponent container = new(); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])); + if (footer is not null) + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); + builder.AddComponents(container); + await context.EditResponseAsync(builder); + await image.DisposeAsync(); + } + + /// + /// Tries to get an image from the Weeb.net API. + /// + /// The context. + /// The data. + /// The stream. + /// Whether the image was successfully retrieved. + public static bool TryGetWeebNetImage(this BaseContext context, RandomData? data, [NotNullWhen(true)] out MemoryStream? stream) + { + if (data is null) + { + stream = null; + return false; + } + + stream = new(context.Client.RestClient.GetByteArrayAsync(data.Url.ResizeLink()).Result); + return true; + } + + /// + /// Responds with an error message. + /// + /// The context. + /// The content. + /// The user. + public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, string content, DiscordUser? user = null) + { + var builder = new DiscordWebhookBuilder().WithContent(content); + if (user is not null) + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + else + builder.WithAllowedMentions([new UserMention(ctx.User)]); + await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + } + + /// + /// Responds with an error message. + /// + /// The context. + public static async Task ImageRespondWithErrorAsync(this BaseContext ctx) + => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get image")); + + /// + /// Responds with an error message if the is null. + /// + /// The context. + /// The image data to check. + /// Whether the image data is not null. + public static async Task CheckForProperImageResultAsync(this BaseContext ctx, [NotNullWhen(true)] ImgData? imgData) + { + if (imgData is not null) + return true; + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get image")); + return false; + } +} diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 922a0db8..7479afb7 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,108 +1,126 @@ -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using MikuSharp.Entities; -using MikuSharp.Enums; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; +/// +/// Provides fixed options for Discord commands. +/// internal class FixedOptionProviders { - internal class RepeatModeProvider : ChoiceProvider + /// + /// Provides choices for repeat modes. + /// + internal sealed class RepeatModeProvider : ChoiceProvider { + /// + /// Provides the choices for repeat modes. + /// + /// A task that represents the asynchronous operation. The task result contains the choices for repeat modes. public override Task> Provider() { var list = new List(3) { - new DiscordApplicationCommandOptionChoice("Off", $"{(int)RepeatMode.Off}"), - new DiscordApplicationCommandOptionChoice("On", $"{(int)RepeatMode.On}"), - new DiscordApplicationCommandOptionChoice("All", $"{(int)RepeatMode.All}"), + new("None", $"{(int)RepeatMode.None}"), + new("All", $"{(int)RepeatMode.All}"), + new("Current", $"{(int)RepeatMode.Current}") }; return Task.FromResult>(list); } } } +/// +/// Provides autocomplete options for Discord commands. +/// internal class AutocompleteProviders { - internal class BanProvider : IAutocompleteProvider + /// + /// Provides autocomplete options for banned users. + /// + internal sealed class BanProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for banned users. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// banned users. + /// public async Task> Provider(AutocompleteContext ctx) { var bans = await ctx.Guild.GetBansAsync(); List bannedUsers = new(25); - if (ctx.FocusedOption.Value == null) - bannedUsers.AddRange(bans.Take(25)); - else - bannedUsers.AddRange(bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); + bannedUsers.AddRange(ctx.FocusedOption.Value is not string value + ? bans.Take(25) + : bans.Where(x => x.User.Username.ToLowerInvariant().Contains(Convert.ToString(value))).Take(25)); - return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithDiscriminator, x.User.Id.ToString())); + return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); } } -/* - internal class PlaylistProvider : IAutocompleteProvider + /// + /// Provides autocomplete options for playlists. + /// + internal sealed class PlaylistProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for playlists. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// playlists. + /// public async Task> Provider(AutocompleteContext ctx) - { - var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (plls.Count == 0) - return new List() { new("You have no songs", "error") }; - var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - - List> playlists = new(25); - if (ctx.FocusedOption.Value == null) - playlists.AddRange(DbPlaylists.Take(25)); - else - playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); - } + => []; } - internal class SongProvider : IAutocompleteProvider + + /// + /// Provides autocomplete options for songs. + /// + internal sealed class SongProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for songs. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// songs. + /// public async Task> Provider(AutocompleteContext ctx) - { - var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); - if (playlist == null) - return new List() { new("You have no playlist selected", "error") }; - if (playlist == "error") - return new List() { new("You have no valid playlist selected", "error") }; - - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var tracks = await pls.GetEntries(); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(tracks.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(tracks.Where(x => x.track.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.track.Title}", x.Position.ToString())); - } + => []; } - internal class QueueProvider : IAutocompleteProvider + /// + /// Provides autocomplete options for the queue. + /// + internal sealed class QueueProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for the queue. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for the + /// queue. + /// public async Task> Provider(AutocompleteContext ctx) { - var queue = await Database.GetQueueAsync(ctx.Guild); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(queue.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(queue.Where(x => x.position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(queue.Where(x => x.track.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + return await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => + { + var value = ctx.FocusedOption.Value?.ToString(); + var queue = musicSession.LavalinkGuildPlayer?.Queue.ToList(); + if (queue is null || queue.Count is 0) + return Task.FromResult>([new("The queue is empty", -1)]); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.position}: {x.track.Title}", x.position.ToString())); + var queueEntries = queue + .Select((entry, index) => (index: index + 1, entry)) + .ToDictionary(x => x.index, x => x.entry); + var filteredQueueEntries = string.IsNullOrEmpty(value) + ? queueEntries.Take(25) + : queueEntries.Where(x => x.Value.Info.Title.ToLowerInvariant().Contains(value.ToLower())).Take(25); + return Task.FromResult(filteredQueueEntries.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key - 1))); + }, null, [new("The queue is empty", -1)]); } - }*/ + } } diff --git a/MikuSharp/Utilities/Formatters.cs b/MikuSharp/Utilities/Formatters.cs new file mode 100644 index 00000000..1aef21a5 --- /dev/null +++ b/MikuSharp/Utilities/Formatters.cs @@ -0,0 +1,64 @@ +namespace MikuSharp.Utilities; + +/// +/// Represents a collection of formatters. +/// +internal static class Formatters +{ + /// + /// Resizes an image link. + /// + /// The url of the image to resize. + /// The resized image. + public static string ResizeLink(this string url) + => $"https://api.meek.moe/im/?image={url}&resize=500"; + + /// + /// Formats a into a human-readable string. + /// + /// The time span to format. + /// The formatted time span. + public static string FormatTimeSpan(this TimeSpan timeSpan) + => timeSpan.TotalHours >= 1 + ? $"{(int)timeSpan.TotalHours:D2}h:{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s" + : timeSpan.TotalMinutes >= 1 + ? $"{(int)timeSpan.TotalMinutes:D2}m:{timeSpan.Seconds:D2}s" + : $"{(int)timeSpan.TotalSeconds:D2}s"; + + /// + /// Maps a source name to an emoji. + /// + /// The source name. + /// The id of the mapped emoji. + public static ulong GetEmojiBasedOnSourceName(this string source) + { +#if DEBUG + return source.ToLowerInvariant() switch + { + "applemusic" => 1336837190805885050, + "yandexmusic" => 1336836824420716545, + "flowerytts" => 1336836652911300680, + "deezer" => 1336836375948824576, + "vkmusic" => 1336836363265511455, + "soundcloud" => 1336836056225681522, + "bandcamp" => 1336835936499011615, + "http" => 1336835679748882433, + "twitch" => 1336835463293702267, + "vimeo" => 1336835333358092411, + "nico" => 1336835252076941342, + "youtube" => 1336834499903881327, + "spotify" => 1336834471277760582, + "local" => 1336838168170987520, + "bilibili" => 1336840488786985013, + _ => 1336839088678113482 + }; +#else + return source.ToLowerInvariant() switch + { + "spotify" => 1336571943687688252, + "youtube" => 1336587088132440115, + _ => 1336624959207903283 + }; +#endif + } +} diff --git a/MikuSharp/Utilities/LavalinkExtensionMethods.cs b/MikuSharp/Utilities/LavalinkExtensionMethods.cs new file mode 100644 index 00000000..9b6f54a2 --- /dev/null +++ b/MikuSharp/Utilities/LavalinkExtensionMethods.cs @@ -0,0 +1,6 @@ +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for Lavalink-related operations. +/// +internal static class LavalinkExtensionMethods; diff --git a/MikuSharp/Utilities/Music.cs b/MikuSharp/Utilities/Music.cs deleted file mode 100644 index 1df08fff..00000000 --- a/MikuSharp/Utilities/Music.cs +++ /dev/null @@ -1,218 +0,0 @@ -/*using AlbumArtExtraction; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using Google.Apis.Services; -using Google.Apis.YouTube.v3; - -using HeyRed.Mime; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Entities; -using MikuSharp.Enums; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Utilities; - -public static class Music -{ - public static ExtService GetExtService(string e) - { - return e switch - { - "Youtube" => ExtService.Youtube, - "Soundcloud" => ExtService.Soundcloud, - _ => ExtService.None - }; - } - - public static string GetPlaybackOptions(this MusicInstance instance) - { - var opts = ""; - if (instance.repeatMode == RepeatMode.On) - opts += DiscordEmoji.FromUnicode("🔂"); - if (instance.repeatMode == RepeatMode.All) - opts += DiscordEmoji.FromUnicode("🔁"); - if (instance.shuffleMode == ShuffleMode.On) - opts += DiscordEmoji.FromUnicode("🔀"); - if (opts == "") - return "None"; - return opts; - } - - public static async Task ConditionalConnect(this Guild guild, InteractionContext ctx) - { - if (!guild.musicInstance.guildConnection?.IsConnected != null && !guild.musicInstance.guildConnection.IsConnected) - return; - await guild.musicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - } - - public static async Task IsNotConnected(this Guild guild, InteractionContext ctx) - { - if (guild.musicInstance == null || guild.musicInstance?.guildConnection?.IsConnected == false) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Not connected!")); - return true; - } - else - return false; - } - - public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? search_or_url) - => !string.IsNullOrEmpty(search_or_url) ? search_or_url : attachment?.ProxyUrl; - - public static void GetPlayingState(this Guild guild, out string time1, out string time2) - { - switch (guild.musicInstance.currentSong.track.Length.Hours) - { - case < 1: - time1 = guild.musicInstance.guildConnection.CurrentState.PlaybackPosition.ToString(@"mm\:ss"); - time2 = guild.musicInstance.currentSong.track.Length.ToString(@"mm\:ss"); - break; - default: - time1 = guild.musicInstance.guildConnection.CurrentState.PlaybackPosition.ToString(@"hh\:mm\:ss"); - time2 = guild.musicInstance.currentSong.track.Length.ToString(@"hh\:mm\:ss"); - break; - } - } - - public static void GetPlayingState(this Entry entry, out string time) - { - time = entry.track.Length.Hours switch - { - < 1 => entry.track.Length.ToString(@"mm\:ss"), - _ => entry.track.Length.ToString(@"hh\:mm\:ss"), - }; - } - - public static async Task GetYoutubePlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - try - { - var youtubeService = new YouTubeService(new BaseClientService.Initializer() - { - ApiKey = MikuBot.Config.YoutubeApiToken, - ApplicationName = typeof(MikuBot).ToString() - }); - var searchListRequest = youtubeService.Search.List("snippet"); - searchListRequest.Q = lastPlayedSongs != null ? lastPlayedSongs[0].track.Title + " " + lastPlayedSongs[0].track.Author : guild.musicInstance.currentSong.track.Title + " " + guild.musicInstance.currentSong.track.Author; - searchListRequest.MaxResults = 1; - searchListRequest.Type = "video"; - var searchListResponse = await searchListRequest.ExecuteAsync(); - - if (lastPlayedSongs == null) - { - guild.GetPlayingState(out var time1, out var time2); - builder.AddField(new DiscordEmbedField($"{guild.musicInstance.currentSong.track.Title} ({time1}/{time2})", $"[Video Link]({guild.musicInstance.currentSong.track.Uri})\n" + - $"[{guild.musicInstance.currentSong.track.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); - } - else - { - lastPlayedSongs[0].GetPlayingState(out var time); - builder.AddField(new DiscordEmbedField($"{lastPlayedSongs[0].track.Title} ({time})", $"[Video Link]({lastPlayedSongs[0].track.Uri})\n" + - $"[{lastPlayedSongs[0].track.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); - } - builder.AddField(new DiscordEmbedField("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 ? - string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") : - searchListResponse.Items[0].Snippet.Description - )); - builder.WithImageUrl(searchListResponse.Items[0].Snippet.Thumbnails.High.Url); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); - } - catch(Exception) - { - if (builder.Fields.Count != 1) - { - if (lastPlayedSongs == null) - builder.AddField(new DiscordEmbedField($"{guild.musicInstance.currentSong.track.Title} ({guild.musicInstance.currentSong.track.Length})", $"By {guild.musicInstance.currentSong.track.Author}\n[Link]({guild.musicInstance.currentSong.track.Uri})\nRequested by <@{guild.musicInstance.currentSong.addedBy}>")); - else - builder.AddField(new DiscordEmbedField($"{lastPlayedSongs[0].track.Title} ({lastPlayedSongs[0].track.Length})", $"By {lastPlayedSongs[0].track.Author}\n[Link]({lastPlayedSongs[0].track.Uri})")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); - } - } - return builder; - } - - public static async Task GetUrlPlayingInformationAsync(this DiscordEmbedBuilder builder, DiscordClient client, Guild guild, List? lastPlayedSongs) - { - Stream? img = null; - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; - try - { - MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.track.Uri)) - { - Position = 0 - }; - FileStream e = File.Create($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); - await d.CopyToAsync(e); - e.Close(); - var selector = new Selector(); - var extractor = selector.SelectAlbumArtExtractor($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); - img = extractor.Extract($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); - } - catch (Exception ex) - { - client.Logger.LogDebug(ex.Message); - client.Logger.LogDebug(ex.StackTrace); - img = null; - File.Delete($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); - } - builder.AddField(new DiscordEmbedField($"{entry.track.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.track.Author}\n[Link]({entry.track.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.musicInstance.currentSong.addedBy}>" : "")}")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); - if (img != null) - builder.WithImageUrl($"attachment://{entry.track.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); - return builder; - } - - public static DiscordEmbedBuilder GetOtherPlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; - builder.AddField(new DiscordEmbedField($"{entry.track.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.track.Author}\n[Link]({entry.track.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.musicInstance.currentSong.addedBy}>" : "")}")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); - return builder; - } - - public static string GetDynamicPlayingState(this Guild guild, List? lastPlayedSongs = null) - { - switch (lastPlayedSongs) - { - case null: - { - guild.GetPlayingState(out var time1, out var time2); - return $"{time1}/{time2}"; - } - - default: - { - lastPlayedSongs[0].GetPlayingState(out var time); - return $"{time}"; - } - } - } - - public static async Task SendPlayingInformationAsync(this InteractionContext ctx, DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; - if (entry == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); - return; - } - builder = entry.track.Uri.ToString().Contains("youtu") - ? await builder.GetYoutubePlayingInformationAsync(guild) - : entry.track.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/") || entry.track.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/") - ? await builder.GetUrlPlayingInformationAsync(ctx.Client, guild, lastPlayedSongs) - : builder.GetOtherPlayingInformationAsync(guild, lastPlayedSongs); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs new file mode 100644 index 00000000..7a0e552b --- /dev/null +++ b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs @@ -0,0 +1,223 @@ +using System.Diagnostics.CodeAnalysis; + +using MikuSharp.Entities; +using MikuSharp.Enums; + +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for use with s. +/// +internal static class MusicSessionExtensionMethods +{ + /// + /// Gets the default session. + /// + /// The lavalink extension. + /// The first session or . + public static LavalinkSession? DefaultSession(this LavalinkExtension lavalink) + => lavalink.ConnectedSessions.Count > 0 ? lavalink.ConnectedSessions.First().Value : null; + + /// + /// Builds a music status embed. + /// + /// The music session. + /// The description. + /// The additional embed fields. + /// The built embed. + public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, string description, List? additionalEmbedFields = null) + { + var builder = new DiscordEmbedBuilder() + .WithAuthor(HatsuneMikuBot.ShardedClient.CurrentUser.UsernameWithGlobalName, iconUrl: HatsuneMikuBot.ShardedClient.CurrentUser.AvatarUrl) + .WithColor(DiscordColor.Black) + .WithTitle("Miku Music Status") + .WithDescription(description); + + builder.AddField(new("State", session.PlaybackState.ToString())); + builder.AddField(new("Repeat Mode", session.RepeatMode.ToString())); + + if (additionalEmbedFields is null) + return builder.Build(); + + ArgumentOutOfRangeException.ThrowIfGreaterThan(additionalEmbedFields.Count, 23, nameof(additionalEmbedFields)); + builder.AddFields(additionalEmbedFields); + + return builder.Build(); + } + + /// + /// Builds a music status embed. + /// + /// The music session. + /// The additional embed fields. + /// The built embed. + public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List? additionalEmbedFields = null) + => session.StatusMessage is not null ? BuildMusicStatusEmbed(session, session.StatusMessage.Embeds[0].Description, additionalEmbedFields) : throw new NullReferenceException(); + + /// + /// Loads and plays an . + /// + /// The music session. + /// The interaction context. + /// The identifier to load. + /// Whether to shuffle playlists. Defaults to . + /// The optional search type. Defaults to . + /// Whether the track was successfully loaded and added to the queue. + /// + public static async Task LoadAndPlayTrackAsync(this MusicSession musicSession, InteractionContext ctx, string identifier, bool shufflePlaylists = false, LavalinkSearchType searchType = LavalinkSearchType.Plain) + { + var loadResult = await musicSession.LavalinkGuildPlayer.LoadTracksAsync(searchType, identifier); + switch (loadResult.LoadType) + { + case LavalinkLoadResultType.Track: + var track = loadResult.GetResultAs(); + musicSession.LavalinkGuildPlayer.AddToQueue(track); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added {track.Info.Title.Bold()} to the queue!")); + break; + case LavalinkLoadResultType.Playlist: + var playlist = loadResult.GetResultAs(); + musicSession.LavalinkGuildPlayer.AddToQueue(playlist); + if (shufflePlaylists) + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added playlist {playlist.Info.Name.Bold()} to the queue.")); + break; + case LavalinkLoadResultType.Search: + var tracks = loadResult.GetResultAs>(); + musicSession.LavalinkGuildPlayer.AddToQueue(tracks.First()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added {tracks.First().Info.Title.Bold()} to the queue!")); + break; + case LavalinkLoadResultType.Empty: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"No results found for `{identifier.InlineCode()}`")); + throw new("No results found"); + case LavalinkLoadResultType.Error: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Something went wrong..\nReason: {loadResult.GetResultAs().Message ?? "unknown"}")); + throw new("Lavalink error"); + default: + throw new ArgumentOutOfRangeException(null, "Could not determine the type of the lavalink search result"); + } + + switch (musicSession.PlaybackState) + { + case PlaybackState.Stopped: + musicSession.LavalinkGuildPlayer.PlayQueue(); + break; + case PlaybackState.Paused: + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + break; + case PlaybackState.Playing: + default: + break; + } + + return musicSession; + } + + /// + /// Executes an action with the music session. + /// + /// The interaction context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func successAction, Func? failureAction = null, Func? finalAction = null) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, finalAction); + } + + /// + /// Executes an action with the music session. + /// + /// The autocomplete context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func successAction, Func? failureAction = null, Func? finalAction = null) + => await ctx.Guild.Id.ExecuteWithMusicSessionAsync(successAction, failureAction, finalAction); + + /// + /// Executes an action with the music session. + /// + /// The guild ID. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func successAction, Func? failureAction = null, Func? finalAction = null) + { + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) + { + if (HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + await successAction(guildId, musicSession); + else if (failureAction is not null) + await failureAction(guildId); + } + + if (finalAction is not null) + await finalAction(guildId); + } + + /// + /// Executes an action with the music session. + /// + /// The type of the result. + /// The interaction context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> successAction, Func>? failureAction = null, T defaultValue = default) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + return await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); + } + + /// + /// Executes an action with the music session. + /// + /// The type of the result. + /// The autocomplete context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func> successAction, Func>? failureAction = null, T defaultValue = default) + => await ctx.Guild.Id.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); + + /// + /// Executes an action with the music session. + /// + /// The type of the result. + /// The guild ID. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func> successAction, Func>? failureAction = null, T defaultValue = default) + { + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) + { + if (HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + return await successAction(guildId, musicSession); + if (failureAction is not null) + return await failureAction(guildId); + } + + return defaultValue; + } +} diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs deleted file mode 100644 index d10115bd..00000000 --- a/MikuSharp/Utilities/NND.cs +++ /dev/null @@ -1,67 +0,0 @@ -/*using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using Microsoft.Extensions.Logging; - -using NicoNicoNii; - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -namespace MikuSharp.Utilities; - -public static class NND -{ - public static async Task GetNNDAsync(this InteractionContext ctx,string n, string s, ulong msg_id) - { - try - { - NNDClient nndClient = new(); - NicoVideoClient videoClient = new(nndClient); - var videoPage = await videoClient.GetWatchPageInfoAsync(s); - var download_exe = "nnd.exe"; - var linux_exe = "nndownload.py"; - string cmd = download_exe; - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); - if (OperatingSystem.IsLinux()) - cmd = linux_exe; - Process downloadProcess = new(); - downloadProcess.StartInfo.FileName = cmd; - downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; - downloadProcess.OutputDataReceived += (d, f) => - { - ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); - }; - downloadProcess.Start(); - await downloadProcess.WaitForExitAsync(); - var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; - var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Converting")); - Process convertProgress = new(); - convertProgress.StartInfo.FileName = "ffmpeg"; - convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; - convertProgress.OutputDataReceived += (d, f) => - { - ctx.Client.Logger.LogDebug("{data}", f.Data); - }; - convertProgress.Start(); - await convertProgress.WaitForExitAsync(); - File.Delete($@"{s}.mp4"); - MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); - File.Delete($@"{s}.mp3"); - ms.Position = 0; - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Encountered error")); - return null; - } - } - -} -*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/NndExtensionMethods.cs b/MikuSharp/Utilities/NndExtensionMethods.cs new file mode 100644 index 00000000..3e47ff67 --- /dev/null +++ b/MikuSharp/Utilities/NndExtensionMethods.cs @@ -0,0 +1,62 @@ +using System.Diagnostics; + +using NicoNicoNii; + +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for NND-related operations. +/// +public static class NndExtensionMethods +{ + /// + /// Gets a NND video. + /// + /// The interaction context. + /// The name of the video. + /// The ID of the video. + /// The ID of the message. + /// The video as a memory stream. + public static async Task GetNndAsync(this InteractionContext ctx, string name, string videoId, ulong msgId) + { + try + { + NndClient nndClient = new(); + NicoVideoClient videoClient = new(nndClient); + var videoPage = await videoClient.GetWatchPageInfoAsync(videoId); + var downloadExe = "nnd.exe"; + var linuxExe = "nndownload.py"; + var cmd = downloadExe; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); + if (OperatingSystem.IsLinux()) + cmd = linuxExe; + Process downloadProcess = new(); + downloadProcess.StartInfo.FileName = cmd; + downloadProcess.StartInfo.Arguments = $"-g -o {$@"{videoId}"}.mp4 {$@"{name}"}"; + downloadProcess.OutputDataReceived += (_, f) => ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); + downloadProcess.Start(); + await downloadProcess.WaitForExitAsync(); + var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; + var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); + Process convertProgress = new(); + convertProgress.StartInfo.FileName = "ffmpeg"; + convertProgress.StartInfo.Arguments = $"-i {$@"{videoId}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{videoId}"}.mp3"; + convertProgress.OutputDataReceived += (_, f) => ctx.Client.Logger.LogDebug("{data}", f.Data); + convertProgress.Start(); + await convertProgress.WaitForExitAsync(); + File.Delete($@"{videoId}.mp4"); + MemoryStream ms = new(await File.ReadAllBytesAsync($@"{videoId}.mp3")); + File.Delete($@"{videoId}.mp3"); + ms.Position = 0; + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); + return null; + } + } +} diff --git a/MikuSharp/Utilities/Old/Database.cs b/MikuSharp/Utilities/Old/Database.cs new file mode 100644 index 00000000..478a322a --- /dev/null +++ b/MikuSharp/Utilities/Old/Database.cs @@ -0,0 +1,353 @@ +/*using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.Entities; +using DisCatSharp.Lavalink.Entities; + +using MikuSharp.Entities; + +using Npgsql; + +namespace MikuSharp.Utilities; + +public class Database +{ + public static async Task AddToLastPlayingListAsync(ulong g, string ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guild_id = '{g.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "ts"; + para2.Value = ts; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "pos"; + para3.Value = position; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task ReorderQueue(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = await GetQueueAsync(g); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + var i = 0; + var longcmd = ""; + + foreach (var qi in queueNow) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; + i++; + } + + var cmd2 = new NpgsqlCommand(); + if (string.IsNullOrEmpty(longcmd)) + return; + + cmd2.CommandText = longcmd; + cmd2.Connection = conn; + var para = cmd2.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd2.Parameters.Add(para); + i = 0; + + foreach (var qi in queueNow) + { + var para2 = cmd2.CreateParameter(); + para2.ParameterName = $"adddate{i}"; + para2.Value = qi.AdditionDate.UtcDateTime; + cmd2.Parameters.Add(para2); + i++; + } + + await cmd2.ExecuteNonQueryAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task RebuildQueue(DiscordGuild g, List q) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = q; + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}'", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + var i = 0; + var longcmd = ""; + + foreach (var qi in queueNow) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; + i++; + } + + var cmd2 = new NpgsqlCommand(); + if (string.IsNullOrEmpty(longcmd)) + return; + + cmd2.CommandText = longcmd; + cmd2.Connection = conn; + var para = cmd2.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd2.Parameters.Add(para); + i = 0; + + foreach (var qi in queueNow) + { + var para2 = cmd2.CreateParameter(); + para2.ParameterName = $"adddate{i}"; + para2.Value = qi.AdditionDate.UtcDateTime; + cmd2.Parameters.Add(para2); + i++; + } + + await cmd2.ExecuteNonQueryAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task> GetQueueAsync(DiscordGuild g) + { + List queue = []; + var count = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd1 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + count = Convert.ToInt32(await cmd1.ExecuteScalarAsync()); + await cmd1.DisposeAsync(); + + if (count == 0) + { + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } + + var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guild_id = '{g.Id.ToString()}' ORDER BY position ASC;", conn); + var reader2 = await cmd2.ExecuteReaderAsync(); + while (await reader2.ReadAsync()) + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader2["track"])), Convert.ToUInt64(reader2["user_id"]), DateTimeOffset.Parse(reader2["added_at"].ToString()), + Convert.ToInt32(reader2["position"]))); + await reader2.CloseAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@guild,@pos,@user,@ts,@time)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "ts"; + para3.Value = ts; + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "pos"; + para4.Value = position; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "time"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var longcmd = ""; + + foreach (var tt in ts) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Encoded}',@time);"; + position++; + } + + var cmd = new NpgsqlCommand(longcmd, conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "time"; + para3.Value = DateTime.UtcNow; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var longcmd = ""; + + foreach (var tt in ts) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Track.Encoded}',@time);"; + position++; + } + + var cmd = new NpgsqlCommand(longcmd, conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "time"; + para3.Value = DateTime.UtcNow; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) + { + var qnow = await GetQueueAsync(g); + qnow.Insert(pos, new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(ts), u, DateTimeOffset.UtcNow, pos)); + await RebuildQueue(g, qnow); + } + + public static async Task InsertToQueue(DiscordGuild g, ulong u, List ts, int pos) + { + var qnow = await GetQueueAsync(g); + foreach (var tt in ts) + qnow.Insert(pos, new(tt, u, DateTimeOffset.UtcNow, pos)); + await RebuildQueue(g, qnow); + } + + public static async Task RemoveFromQueueAsync(int position, DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await ReorderQueue(g); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task ClearQueue(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task MoveQueueItems(DiscordGuild g, int oldpos, int newpos) + { + var qnow = await GetQueueAsync(g); + var temp = qnow[oldpos]; + qnow.RemoveAt(oldpos); + qnow.Insert(newpos, temp); + await RebuildQueue(g, qnow); + } + + public static async Task> GetLastPlayingListAsync(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guild_id = '{g.Id.ToString()}' ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); + var reader = await cmd2.ExecuteReaderAsync(); + List queue = []; + while (await reader.ReadAsync()) + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync((string)reader["trackstring"]), DateTimeOffset.UtcNow)); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } +} +*/ + + diff --git a/MikuSharp/Utilities/Old/Music.cs b/MikuSharp/Utilities/Old/Music.cs new file mode 100644 index 00000000..c890095c --- /dev/null +++ b/MikuSharp/Utilities/Old/Music.cs @@ -0,0 +1,245 @@ +/*using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using AlbumArtExtraction; + +using DisCatSharp; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; + +using Google.Apis.YouTube.v3; + +using HeyRed.Mime; + +using Microsoft.Extensions.Logging; + +using MikuSharp.Entities; +using MikuSharp.Enums; + +namespace MikuSharp.Utilities; + +public static class Music +{ + public static ExtService GetExtService(string e) + { + return e switch + { + "Youtube" => ExtService.Youtube, + "Soundcloud" => ExtService.Soundcloud, + _ => ExtService.None + }; + } + + public static string GetPlaybackOptions(this MusicInstance instance) + { + string opts = null; + + switch (instance.RepeatMode) + { + case RepeatMode.On: + opts += DiscordEmoji.FromUnicode("🔂"); + break; + case RepeatMode.All: + opts += DiscordEmoji.FromUnicode("🔁"); + break; + } + + if (instance.ShuffleMode == ShuffleMode.On) + opts += DiscordEmoji.FromUnicode("🔀"); + + return opts ?? "None"; + } + + public static async Task ConditionalConnect(this Guild guild, InteractionContext ctx) + { + if (!guild.MusicInstance?.GuildConnection?.IsConnected != null && !guild.MusicInstance.GuildConnection.IsConnected) + return; + + await guild.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); + } + + public static async Task IsNotConnected(this Guild guild, InteractionContext ctx) + { + if (guild.MusicInstance == null! || guild.MusicInstance?.GuildConnection?.IsConnected == false) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Not connected!")); + return true; + } + + return false; + } + + public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? searchOrUrl) + => !string.IsNullOrEmpty(searchOrUrl) + ? searchOrUrl + : attachment?.ProxyUrl; + + public static void GetPlayingState(this Guild guild, out string time1, out string time2) + { + switch (guild.MusicInstance.CurrentSong.Track.Info.Length.Hours) + { + case < 1: + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"mm\:ss"); + break; + default: + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"hh\:mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"hh\:mm\:ss"); + break; + } + } + + public static void GetPlayingState(this Entry entry, out string time) + { + time = entry.Track.Info.Length.Hours switch + { + < 1 => entry.Track.Info.Length.ToString(@"mm\:ss"), + _ => entry.Track.Info.Length.ToString(@"hh\:mm\:ss") + }; + } + + public static async Task GetYoutubePlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + try + { + var youtubeService = new YouTubeService(new() + { + ApiKey = MikuBot.Config.YoutubeApiToken, + ApplicationName = typeof(MikuBot).ToString() + }); + var searchListRequest = youtubeService.Search.List("snippet"); + searchListRequest.Q = lastPlayedSongs != null + ? lastPlayedSongs[0].Track.Info.Title + " " + lastPlayedSongs[0].Track.Info.Author + : guild.MusicInstance.CurrentSong.Track.Info.Title + " " + guild.MusicInstance.CurrentSong.Track.Info.Author; + searchListRequest.MaxResults = 1; + searchListRequest.Type = "video"; + var searchListResponse = await searchListRequest.ExecuteAsync(); + + if (lastPlayedSongs == null) + { + guild.GetPlayingState(out var time1, out var time2); + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({time1}/{time2})", + $"[Video Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\n" + $"[{guild.MusicInstance.CurrentSong.Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + } + else + { + lastPlayedSongs[0].GetPlayingState(out var time); + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({time})", + $"[Video Link]({lastPlayedSongs[0].Track.Info.Uri})\n" + $"[{lastPlayedSongs[0].Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + } + + builder.AddField(new("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 + ? string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") + : searchListResponse.Items[0].Snippet.Description + )); + builder.WithImageUrl(searchListResponse.Items[0].Snippet.Thumbnails.High.Url); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + } + catch (Exception) + { + if (builder.Fields.Count != 1) + { + if (lastPlayedSongs == null) + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({guild.MusicInstance.CurrentSong.Track.Info.Length})", + $"By {guild.MusicInstance.CurrentSong.Track.Info.Author}\n[Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\nRequested by <@{guild.MusicInstance.CurrentSong.AddedBy}>")); + else + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({lastPlayedSongs[0].Track.Info.Length})", $"By {lastPlayedSongs[0].Track.Info.Author}\n[Link]({lastPlayedSongs[0].Track.Info.Uri})")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + } + } + + return builder; + } + + public static async Task GetUrlPlayingInformationAsync(this DiscordEmbedBuilder builder, DiscordClient client, Guild guild, List? lastPlayedSongs) + { + Stream? img = null; + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + + try + { + MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.Track.Info.Uri)) + { + Position = 0 + }; + var e = File.Create($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + await d.CopyToAsync(e); + e.Close(); + var selector = new Selector(); + var extractor = selector.SelectAlbumArtExtractor($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + img = extractor.Extract($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + } + catch (Exception ex) + { + client.Logger.LogDebug(ex.Message); + client.Logger.LogDebug(ex.StackTrace); + img = null; + File.Delete($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + } + + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", + $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + if (img != null) + builder.WithImageUrl($"attachment://{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); + return builder; + } + + public static DiscordEmbedBuilder GetOtherPlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", + $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + return builder; + } + + public static string GetDynamicPlayingState(this Guild guild, List? lastPlayedSongs = null) + { + switch (lastPlayedSongs) + { + case null: + { + guild.GetPlayingState(out var time1, out var time2); + return $"{time1}/{time2}"; + } + + default: + { + lastPlayedSongs[0].GetPlayingState(out var time); + return $"{time}"; + } + } + } + + public static async Task SendPlayingInformationAsync(this InteractionContext ctx, DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + + if (entry == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); + return; + } + + builder = entry.Track.Info.Uri.ToString().Contains("youtu") + ? await builder.GetYoutubePlayingInformationAsync(guild) + : entry.Track.Info.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/", StringComparison.Ordinal) || entry.Track.Info.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/", StringComparison.Ordinal) + ? await builder.GetUrlPlayingInformationAsync(ctx.Client, guild, lastPlayedSongs) + : builder.GetOtherPlayingInformationAsync(guild, lastPlayedSongs); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); + } +} +*/ + + diff --git a/MikuSharp/Utilities/Old/PlaylistDB.cs b/MikuSharp/Utilities/Old/PlaylistDB.cs new file mode 100644 index 00000000..6d051d45 --- /dev/null +++ b/MikuSharp/Utilities/Old/PlaylistDB.cs @@ -0,0 +1,625 @@ +/*using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Enums; +using DisCatSharp.Interactivity.Extensions; +using DisCatSharp.Lavalink; + +using FluentFTP; + +using MikuSharp.Entities; +using MikuSharp.Enums; + +using Npgsql; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace MikuSharp.Utilities; + +public class PlaylistDB +{ + public static async Task> GetPlaylists(DiscordGuild guild, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); + var reader = await cmd2.ExecuteReaderAsync(); + Dictionary lists = new(); + while (await reader.ReadAsync()) + { + lists.Add(Convert.ToString(reader["playlistname"]), await GetPlaylist(guild, u, Convert.ToString(reader["playlistname"]))); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + return lists; + } + + public static async Task> GetPlaylistsSimple(ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); + var reader = await cmd2.ExecuteReaderAsync(); + List lists = new(); + while (await reader.ReadAsync()) + { + lists.Add(Convert.ToString(reader["playlistname"])); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + return lists; + } + + public static async Task GetPlaylist(DiscordGuild guild, ulong u, string p) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + int am = 0; + var cmd = new NpgsqlCommand($"SELECT COUNT(*)" + + $"FROM playlistentries " + + $"WHERE userid = {u} " + + $"AND playlistname = @pl;", conn); + cmd.Parameters.AddWithValue("pl", p); + var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + am = Convert.ToInt32(reader["count"]); + } + reader.Close(); + cmd.Dispose(); + var cmd2 = new NpgsqlCommand($"SELECT *" + + $"FROM playlists " + + $"WHERE userid = {u} " + + $"AND playlistname = @pl;", conn); + var para = cmd2.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd2.Parameters.Add(para); + var reader2 = await cmd2.ExecuteReaderAsync(); + Playlist pl = null; + while (await reader2.ReadAsync()) + { + if (Music.GetExtService(Convert.ToString(reader2["extservice"])) != ExtService.None) + { + try + { + var ss = await MikuBot.LavalinkNodeConnections.First().Value.Rest.GetTracksAsync(new Uri(Convert.ToString(reader2["url"]))); + am = ss.Tracks.Count; + } + catch { } + } + pl = new Playlist(Music.GetExtService(Convert.ToString(reader2["extservice"])), Convert.ToString(reader2["url"]), Convert.ToString(reader2["playlistname"]), Convert.ToUInt64(reader2["userid"]), am, DateTimeOffset.Parse(Convert.ToString(reader2["creation"])), DateTimeOffset.Parse(Convert.ToString(reader2["changed"]))); + } + reader2.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + if (pl == null) throw new Exception("Tf is up? " + p); + return pl; + } + + public static async Task ReorderList(DiscordGuild guild, string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var listNow = await GetPlaylist(guild, u, p); + var ln = await listNow.GetEntries(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + int i = 0; + string longcmd = ""; + foreach (var qi in ln) + { + string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; + string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; + longcmd += $"INSERT INTO playlistentries VALUES ({i},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; + i++; + } + var cmd2 = new NpgsqlCommand(longcmd, conn); + var para1 = cmd2.CreateParameter(); + para1.ParameterName = "p"; + para1.Value = p; + cmd2.Parameters.Add(para1); + var para2 = cmd2.CreateParameter(); + para2.ParameterName = "u"; + para2.Value = Convert.ToInt64(u); + cmd2.Parameters.Add(para2); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task RebuildList(ulong u, string p, List q) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = q; + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + string longcmd = ""; + foreach (var qi in queueNow) + { + string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; + string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; + longcmd += $"INSERT INTO playlistentries VALUES ({qi.Position},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; + } + var cmd2 = new NpgsqlCommand(longcmd, conn); + var para1 = cmd2.CreateParameter(); + para1.ParameterName = "p"; + para1.Value = p; + cmd2.Parameters.Add(para1); + var para2 = cmd2.CreateParameter(); + para2.ParameterName = "u"; + para2.Value = Convert.ToInt64(u); + cmd2.Parameters.Add(para2); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task AddPlaylist(string p, ulong u, ExtService e = ExtService.None, string url = "") + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand("INSERT INTO playlists VALUES (@u,@p,@url,@ext,@cre,@mody)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "u"; + para.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "url"; + para3.Value = url; + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "ext"; + para4.Value = e.ToString(); + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "cre"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + var para6 = cmd.CreateParameter(); + para6.ParameterName = "mody"; + para6.Value = DateTime.UtcNow; + cmd.Parameters.Add(para6); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task RemovePlaylist(string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlists WHERE playlistname = @pl AND userid = {u};", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + await ClearList(p, u); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task AddEntry(string p, ulong u, string ts) + { + int position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + cmd2.Parameters.AddWithValue("pl", p); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + position = reader.GetInt32(0); + } + reader.Close(); + cmd2.Dispose(); + var cmd = new NpgsqlCommand("INSERT INTO playlistentries VALUES (@pos,@p,@u,@ts,@add,@mody);UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pos"; + para.Value = position; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "u"; + para3.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "ts"; + para4.Value = ts; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "add"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + var para6 = cmd.CreateParameter(); + para6.ParameterName = "mody"; + para6.Value = DateTime.UtcNow; + cmd.Parameters.Add(para6); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task AddEntry(string p, ulong u, List ts) + { + int position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd2.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd2.Parameters.Add(para); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + position = reader.GetInt32(0); + } + reader.Close(); + cmd2.Dispose(); + string longcmd = "UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;"; + foreach (var tt in ts) + { + longcmd += $"INSERT INTO playlistentries VALUES ({position},@p,@u,'{tt.TrackString}',@add,@mody);"; + position++; + } + var cmd = new NpgsqlCommand(longcmd, conn); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "u"; + para3.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "add"; + para4.Value = DateTime.UtcNow; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "mody"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, string ts, int pos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(ts), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); + await RebuildList(u, p, q); + } + + public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, List ts, int pos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + foreach (var tt in ts) + { + q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(tt.TrackString), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); + } + await RebuildList(u, p, q); + } + + public static async Task ClearList(string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task MoveListItems(DiscordGuild guild, string p, ulong u, int oldpos, int newpos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + //(q[newpos], q[oldpos]) = (q[oldpos], q[newpos]); + List tempQ = new(q.Count); + List newQ = new(q.Count); + foreach(var entry in q) + { + if (entry.Position == oldpos) + entry.Position = newpos; + else if (entry.Position == newpos) + entry.Position = oldpos; + else + entry.Position = entry.Position; + tempQ.Add(entry); + } + newQ.AddRange(tempQ.OrderBy(x => x.Position)); + await RebuildList(u, p, newQ); + } + + public static async Task RemoveFromList(DiscordGuild guild, int position, string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE pos = {position} AND userid = {u} AND playlistname = @pl;UPDATE playlists SET changed=@mody WHERE userid= {u} AND playlistname=@pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "mody"; + para2.Value = DateTime.UtcNow; + cmd.Parameters.Add(para2); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + await ReorderList(guild, p, u); + conn.Close(); + conn.Dispose(); + } + + public static async Task RenameList(string p, ulong u, string newname) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"UPDATE playlists SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "newn"; + para2.Value = newname; + cmd.Parameters.Add(para2); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + var cmd2 = new NpgsqlCommand($"UPDATE playlistentries SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;UPDATE playlists SET changed = @mody WHERE userid= {u} AND playlistname = @newn;", conn); + var para3 = cmd2.CreateParameter(); + para3.ParameterName = "pl"; + para3.Value = p; + cmd2.Parameters.Add(para3); + var para4 = cmd2.CreateParameter(); + para4.ParameterName = "newn"; + para4.Value = newname; + cmd2.Parameters.Add(para4); + var para5 = cmd2.CreateParameter(); + para5.ParameterName = "mody"; + para5.Value = DateTime.UtcNow; + cmd2.Parameters.Add(para5); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } + + public static async Task GetSong(string n, InteractionContext ctx) + { + var nodeConnection = MikuBot.LavalinkNodeConnections.First().Value; + var inter = ctx.Client.GetInteractivity(); + if (n.ToLower().StartsWith("http://nicovideo.jp") + || n.ToLower().StartsWith("http://sp.nicovideo.jp") + || n.ToLower().StartsWith("https://nicovideo.jp") + || n.ToLower().StartsWith("https://sp.nicovideo.jp") + || n.ToLower().StartsWith("http://www.nicovideo.jp") + || n.ToLower().StartsWith("https://www.nicovideo.jp")) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); + var split = n.Split("/".ToCharArray()); + var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; + FtpClient ftpClient = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + ftpClient.Connect(); + if (!ftpClient.FileExists($"{nndID}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); + ftpClient.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); + } + var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); + return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); + } + else if (n.StartsWith("http://") | n.StartsWith("https://")) + { + var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); + switch (s.LoadResultType) + { + case LavalinkLoadResultType.LoadFailed: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + + "> Playlist is set to private or unlisted\n" + + "> The song is unavailable/deleted").Build())); + return null; + }; + case LavalinkLoadResultType.NoMatches: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); + return null; + }; + case LavalinkLoadResultType.PlaylistLoaded: + { + if (s.PlaylistInfo.SelectedTrack == -1) + { + List buttons = new(2) + { + new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), + new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") + }; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Playlist link detected!") + .WithDescription("Choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); + return new TrackResult(s.PlaylistInfo, s.Tracks); + } + else + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + else + { + List buttons = new(3) + { + new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), + new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), + new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") + }; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Link with Playlist detected!") + .WithDescription("Please choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); + return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); + } + else if (resp.Result.Id == "all") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); + return new TrackResult(s.PlaylistInfo, s.Tracks); + } + else + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + }; + default: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); + return new TrackResult(s.PlaylistInfo, s.Tracks.First()); + }; + } + } + else + { + var s = await nodeConnection.Rest.GetTracksAsync(n); + switch (s.LoadResultType) + { + case LavalinkLoadResultType.LoadFailed: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + + "> The song is unavailable/deleted").Build())); + return null; + }; + case LavalinkLoadResultType.NoMatches: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); + return null; + }; + default: + { + int leng = s.Tracks.Count; + if (leng > 5) leng = 5; + List selectOptions = new(leng) + { + + }; + DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); + var em = new DiscordEmbedBuilder() + .WithTitle("Results!") + .WithDescription("Please select a track:\n") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + for (int i = 0; i < leng; i++) + { + em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); + } + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); + var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); + return null; + } + var trackSelect = Convert.ToInt32(resp.Result.Values.First()); + var track = s.Tracks.ElementAt(trackSelect); + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Choosed {track.Title}")); + + return new TrackResult(s.PlaylistInfo, track); + }; + } + } + } +} +*/ + + diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs deleted file mode 100644 index 9040a225..00000000 --- a/MikuSharp/Utilities/Other.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using System.Threading.Tasks; - -namespace MikuSharp.Utilities; - -public static class Other -{ - public static string resizeLink(string url) - { - return $"https://api.meek.moe/im/?image={url}&resize=500"; - } - - public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) - { - var builder = new DiscordInteractionResponseBuilder(); - if (ephemeral) - { - builder.AsEphemeral(); - } - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - } -} diff --git a/MikuSharp/Utilities/PlaylistDB.cs b/MikuSharp/Utilities/PlaylistDB.cs deleted file mode 100644 index 24cafd4f..00000000 --- a/MikuSharp/Utilities/PlaylistDB.cs +++ /dev/null @@ -1,623 +0,0 @@ -/*using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; - -using FluentFTP; - -using MikuSharp.Entities; -using MikuSharp.Enums; - -using Npgsql; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace MikuSharp.Utilities; - -public class PlaylistDB -{ - public static async Task> GetPlaylists(DiscordGuild guild, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - Dictionary lists = new(); - while (await reader.ReadAsync()) - { - lists.Add(Convert.ToString(reader["playlistname"]), await GetPlaylist(guild, u, Convert.ToString(reader["playlistname"]))); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return lists; - } - - public static async Task> GetPlaylistsSimple(ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List lists = new(); - while (await reader.ReadAsync()) - { - lists.Add(Convert.ToString(reader["playlistname"])); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return lists; - } - - public static async Task GetPlaylist(DiscordGuild guild, ulong u, string p) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - int am = 0; - var cmd = new NpgsqlCommand($"SELECT COUNT(*)" + - $"FROM playlistentries " + - $"WHERE userid = {u} " + - $"AND playlistname = @pl;", conn); - cmd.Parameters.AddWithValue("pl", p); - var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - am = Convert.ToInt32(reader["count"]); - } - reader.Close(); - cmd.Dispose(); - var cmd2 = new NpgsqlCommand($"SELECT *" + - $"FROM playlists " + - $"WHERE userid = {u} " + - $"AND playlistname = @pl;", conn); - var para = cmd2.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd2.Parameters.Add(para); - var reader2 = await cmd2.ExecuteReaderAsync(); - Playlist pl = null; - while (await reader2.ReadAsync()) - { - if (Music.GetExtService(Convert.ToString(reader2["extservice"])) != ExtService.None) - { - try - { - var ss = await MikuBot.LavalinkNodeConnections.First().Value.Rest.GetTracksAsync(new Uri(Convert.ToString(reader2["url"]))); - am = ss.Tracks.Count; - } - catch { } - } - pl = new Playlist(Music.GetExtService(Convert.ToString(reader2["extservice"])), Convert.ToString(reader2["url"]), Convert.ToString(reader2["playlistname"]), Convert.ToUInt64(reader2["userid"]), am, DateTimeOffset.Parse(Convert.ToString(reader2["creation"])), DateTimeOffset.Parse(Convert.ToString(reader2["changed"]))); - } - reader2.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - if (pl == null) throw new Exception("Tf is up? " + p); - return pl; - } - - public static async Task ReorderList(DiscordGuild guild, string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var listNow = await GetPlaylist(guild, u, p); - var ln = await listNow.GetEntries(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - int i = 0; - string longcmd = ""; - foreach (var qi in ln) - { - string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; - string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; - longcmd += $"INSERT INTO playlistentries VALUES ({i},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; - i++; - } - var cmd2 = new NpgsqlCommand(longcmd, conn); - var para1 = cmd2.CreateParameter(); - para1.ParameterName = "p"; - para1.Value = p; - cmd2.Parameters.Add(para1); - var para2 = cmd2.CreateParameter(); - para2.ParameterName = "u"; - para2.Value = Convert.ToInt64(u); - cmd2.Parameters.Add(para2); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task RebuildList(ulong u, string p, List q) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = q; - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - string longcmd = ""; - foreach (var qi in queueNow) - { - string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; - string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; - longcmd += $"INSERT INTO playlistentries VALUES ({qi.Position},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; - } - var cmd2 = new NpgsqlCommand(longcmd, conn); - var para1 = cmd2.CreateParameter(); - para1.ParameterName = "p"; - para1.Value = p; - cmd2.Parameters.Add(para1); - var para2 = cmd2.CreateParameter(); - para2.ParameterName = "u"; - para2.Value = Convert.ToInt64(u); - cmd2.Parameters.Add(para2); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task AddPlaylist(string p, ulong u, ExtService e = ExtService.None, string url = "") - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand("INSERT INTO playlists VALUES (@u,@p,@url,@ext,@cre,@mody)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "u"; - para.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "url"; - para3.Value = url; - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "ext"; - para4.Value = e.ToString(); - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "cre"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - var para6 = cmd.CreateParameter(); - para6.ParameterName = "mody"; - para6.Value = DateTime.UtcNow; - cmd.Parameters.Add(para6); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task RemovePlaylist(string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlists WHERE playlistname = @pl AND userid = {u};", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - await ClearList(p, u); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task AddEntry(string p, ulong u, string ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - cmd2.Parameters.AddWithValue("pl", p); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - var cmd = new NpgsqlCommand("INSERT INTO playlistentries VALUES (@pos,@p,@u,@ts,@add,@mody);UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pos"; - para.Value = position; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "u"; - para3.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "ts"; - para4.Value = ts; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "add"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - var para6 = cmd.CreateParameter(); - para6.ParameterName = "mody"; - para6.Value = DateTime.UtcNow; - cmd.Parameters.Add(para6); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task AddEntry(string p, ulong u, List ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd2.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd2.Parameters.Add(para); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - string longcmd = "UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;"; - foreach (var tt in ts) - { - longcmd += $"INSERT INTO playlistentries VALUES ({position},@p,@u,'{tt.TrackString}',@add,@mody);"; - position++; - } - var cmd = new NpgsqlCommand(longcmd, conn); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "u"; - para3.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "add"; - para4.Value = DateTime.UtcNow; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "mody"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, string ts, int pos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(ts), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); - await RebuildList(u, p, q); - } - - public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, List ts, int pos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - foreach (var tt in ts) - { - q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(tt.TrackString), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); - } - await RebuildList(u, p, q); - } - - public static async Task ClearList(string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task MoveListItems(DiscordGuild guild, string p, ulong u, int oldpos, int newpos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - //(q[newpos], q[oldpos]) = (q[oldpos], q[newpos]); - List tempQ = new(q.Count); - List newQ = new(q.Count); - foreach(var entry in q) - { - if (entry.Position == oldpos) - entry.Position = newpos; - else if (entry.Position == newpos) - entry.Position = oldpos; - else - entry.Position = entry.Position; - tempQ.Add(entry); - } - newQ.AddRange(tempQ.OrderBy(x => x.Position)); - await RebuildList(u, p, newQ); - } - - public static async Task RemoveFromList(DiscordGuild guild, int position, string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE pos = {position} AND userid = {u} AND playlistname = @pl;UPDATE playlists SET changed=@mody WHERE userid= {u} AND playlistname=@pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "mody"; - para2.Value = DateTime.UtcNow; - cmd.Parameters.Add(para2); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - await ReorderList(guild, p, u); - conn.Close(); - conn.Dispose(); - } - - public static async Task RenameList(string p, ulong u, string newname) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"UPDATE playlists SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "newn"; - para2.Value = newname; - cmd.Parameters.Add(para2); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - var cmd2 = new NpgsqlCommand($"UPDATE playlistentries SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;UPDATE playlists SET changed = @mody WHERE userid= {u} AND playlistname = @newn;", conn); - var para3 = cmd2.CreateParameter(); - para3.ParameterName = "pl"; - para3.Value = p; - cmd2.Parameters.Add(para3); - var para4 = cmd2.CreateParameter(); - para4.ParameterName = "newn"; - para4.Value = newname; - cmd2.Parameters.Add(para4); - var para5 = cmd2.CreateParameter(); - para5.ParameterName = "mody"; - para5.Value = DateTime.UtcNow; - cmd2.Parameters.Add(para5); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - - public static async Task GetSong(string n, InteractionContext ctx) - { - var nodeConnection = MikuBot.LavalinkNodeConnections.First().Value; - var inter = ctx.Client.GetInteractivity(); - if (n.ToLower().StartsWith("http://nicovideo.jp") - || n.ToLower().StartsWith("http://sp.nicovideo.jp") - || n.ToLower().StartsWith("https://nicovideo.jp") - || n.ToLower().StartsWith("https://sp.nicovideo.jp") - || n.ToLower().StartsWith("http://www.nicovideo.jp") - || n.ToLower().StartsWith("https://www.nicovideo.jp")) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); - var split = n.Split("/".ToCharArray()); - var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; - FtpClient ftpClient = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - ftpClient.Connect(); - if (!ftpClient.FileExists($"{nndID}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); - ftpClient.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); - } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); - } - else if (n.StartsWith("http://") | n.StartsWith("https://")) - { - var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> Playlist is set to private or unlisted\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); - return null; - }; - case LavalinkLoadResultType.PlaylistLoaded: - { - if (s.PlaylistInfo.SelectedTrack == -1) - { - List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), - new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Playlist link detected!") - .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - else - { - List buttons = new(3) - { - new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), - new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Link with Playlist detected!") - .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); - return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); - } - else if (resp.Result.Id == "all") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - }; - default: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); - return new TrackResult(s.PlaylistInfo, s.Tracks.First()); - }; - } - } - else - { - var s = await nodeConnection.Rest.GetTracksAsync(n); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); - return null; - }; - default: - { - int leng = s.Tracks.Count; - if (leng > 5) leng = 5; - List selectOptions = new(leng) - { - - }; - DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); - var em = new DiscordEmbedBuilder() - .WithTitle("Results!") - .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); - for (int i = 0; i < leng; i++) - { - em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); - } - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); - var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); - return null; - } - var trackSelect = Convert.ToInt32(resp.Result.Values.First()); - var track = s.Tracks.ElementAt(trackSelect); - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Choosed {track.Title}")); - - return new TrackResult(s.PlaylistInfo, track); - }; - } - } - } -} -*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs deleted file mode 100644 index 86e2a5e8..00000000 --- a/MikuSharp/Utilities/Web.cs +++ /dev/null @@ -1,83 +0,0 @@ -using DisCatSharp.Entities; - -using HeyRed.Mime; - -using MikuSharp.Entities; - -using Newtonsoft.Json; - -using System.IO; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; - -using Weeb.net; - -namespace MikuSharp.Utilities; - -public static class Web -{ - public static async Task GetNekosLifeAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.Url))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekos.life"); - dl.Embed = em.Build(); - return dl; - } - - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", MikuBot.Config.KsoftSiToken); - var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); - MemoryStream img = new(await client.GetByteArrayAsync(Other.resizeLink(v.url))); - v.Data = img; - v.Filetype = MimeGuesser.GuessExtension(img); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{v.Filetype}"); - em.WithFooter("by KSoft.si"); - v.Embed = em.Build(); - return v; - } - - public static async Task GetNekobotAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.message))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekobot.xyz"); - dl.Embed = em.Build(); - return dl; - } - - public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) - { - var weeurl = await MikuBot._weebClient.GetRandomAsync(query, tags, nsfw: nsfw); - MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) - { - Position = 0 - }; - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by weeb.sh"); - return new WeebSh - { - ImgData = img, - Extension = MimeGuesser.GuessExtension(img), - Embed = em - }; - } -} diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs new file mode 100644 index 00000000..54a7a69f --- /dev/null +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -0,0 +1,173 @@ +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; + +using HeyRed.Mime; + +using MikuSharp.Entities; + +using Weeb.net; + +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for web-related operations. +/// +public static class WebExtensionMethods +{ + /// + /// Gets a random image from nekos.life. + /// + /// The http client. + /// The url. + /// The nekos.life response. + public static async Task GetNekosLifeAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (dl is null) + return null; + + MemoryStream str = new(await client.GetByteArrayAsync(dl.Url.ResizeLink())) + { + Position = 0 + }; + dl.Data = str; + dl.FileType = MimeGuesser.GuessExtension(str); + return dl; + } + + /// + /// Generates an image using the Nekobot API. + /// + /// The context. + /// The type of image to generate. + /// The parameters. + public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string type, Dictionary parameters) + { + var query = string.Join("&", parameters.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}")); + var response = await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type={type}&{query}"); + var result = JsonConvert.DeserializeObject(response); + + if (result is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + MemoryStream stream = new(await ctx.Client.RestClient.GetByteArrayAsync(result.Message.ResizeLink())) + { + Position = 0 + }; + + await ctx.SendActionMessageAsync(stream); + + await stream.DisposeAsync(); + } + + /// + /// Gets a random image from nekobot.xyz. + /// + /// The http client. + /// The url. + /// The nekobot response. + public static async Task GetNsfwNekobotAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (dl is null) + return null; + + MemoryStream str = new(await client.GetByteArrayAsync(dl.Message.ResizeLink())) + { + Position = 0 + }; + dl.Data = str; + dl.FileType = MimeGuesser.GuessExtension(str); + return dl; + } + + /// + /// Gets an image from meek.moe. + /// + /// The http client. + /// The url. + /// The meek.moe response. + public static async Task GetMeekMoeAsync(this HttpClient client, string url) + { + var mm = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (mm is null) + return null; + + var img = new MemoryStream(await client.GetByteArrayAsync(mm.Url.ResizeLink())) + { + Position = 0 + }; + + mm.Data = img; + mm.FileType = MimeGuesser.GuessExtension(img); + return mm; + } + + /// + /// Gets a random image from weeb.sh. + /// + /// The http client. + /// The query. + /// The optional tags. + /// Whether the search should include NSFW results. + /// The weeb.sh response. + public static async Task GetWeebShAsync(this HttpClient client, string query, IEnumerable? tags = null, NsfwSearch nsfw = NsfwSearch.False) + { + var dl = await HatsuneMikuBot.WeebClient.GetRandomAsync(query, tags ?? [""], nsfw: nsfw); + if (dl is null) + return null; + + MemoryStream img = new(await client.GetByteArrayAsync(dl.Url)) + { + Position = 0 + }; + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by weeb.sh"); + return new() + { + ImgData = img, + Extension = MimeGuesser.GuessExtension(img), + Embed = em + }; + } + + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The stream. + /// Whether the image was successfully retrieved. + public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out MemoryStream? stream) + { + if (data is null) + { + stream = null; + return false; + } + + stream = data.ImgData; + return true; + } + + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The image. + /// Whether the image was successfully retrieved. + public static bool TryGetMeekMoeImage(this MeekMoeImage? data, [NotNullWhen(true)] out MeekMoeImage? image) + { + if (data is null) + { + image = null; + return false; + } + + image = data; + return true; + } +} diff --git a/MikuSharp/config.example.json b/MikuSharp/config.example.json index f7331582..4deb8114 100644 --- a/MikuSharp/config.example.json +++ b/MikuSharp/config.example.json @@ -1,27 +1,27 @@ { - "discordToken": "", - "discordBotListToken": "", - "weebShToken": "", - "youtubeApiToken": "", - "ksoftSiToken": "", - "dbConfig": { - "hostname": "", - "user": "", - "password": "", - "database": "" - }, - "lavaConfig": { - "hostname": "", - "password": "", - "port": 2333 - }, - "nndConfig": { - "mail": "", - "password": "", - "ftpConfig": { - "hostname": "", - "user": "", - "password": "" - } - } -} \ No newline at end of file + "discordToken": "", + "discordBotListToken": "", + "weebShToken": "", + "youtubeApiToken": "", + "ksoftSiToken": "", + "dbConfig": { + "hostname": "", + "user": "", + "password": "", + "database": "" + }, + "lavaConfig": { + "hostname": "", + "password": "", + "port": 2333 + }, + "nndConfig": { + "mail": "", + "password": "", + "ftpConfig": { + "hostname": "", + "user": "", + "password": "" + } + } +} diff --git a/NicoNicoNii b/NicoNicoNii index f3b1aab6..bfe0f5a7 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit f3b1aab659dc729baca3b240df5b003f58979fea +Subproject commit bfe0f5a7a004b785b6af8fffa30cbe424351cb16 diff --git a/build_with_dotnet_publish.sh b/build_with_dotnet_publish.sh new file mode 100755 index 00000000..88cb3a2c --- /dev/null +++ b/build_with_dotnet_publish.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet publish -c Release -r linux-x64 --self-contained=true -p:PublishSingleFile=false -p:GenerateRuntimeConfigurationFiles=true \ No newline at end of file diff --git a/hatsune-miku.service b/hatsune-miku.service new file mode 100644 index 00000000..13450947 --- /dev/null +++ b/hatsune-miku.service @@ -0,0 +1,19 @@ +[Unit] +Description=Hatsune Miku Bot +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart="/root/mikuBotNet8/MikuSharp/MikuSharp/bin/Release/net7.0/linux-x64/publish/MikuSharp" +WorkingDirectory=/root/mikuBotNet8/MikuSharp/MikuSharp/bin/Release/net7.0/linux-x64/publish +Restart=always +RestartSec=10 +KillSignal=SIGINT +Environment=ASPNETCORE_ENVIRONMENT=Production +Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false +SyslogIdentifier=HastuneMikuBot +User=root +Environment=DOTNET_ROOT=/usr/bin/dotnet + +[Install] +WantedBy=multi-user.target \ No newline at end of file