[[[ release 0.4.2.11 ]]]

## 🧯Bug Fix

- 修复 /user 命令在遇到无 username 用户时会将 java.null 转译为值而非报空的问题
- ((隐藏问题)) 为所有消息发送补全特殊字符转义,修复当拼接内容有 html 特殊字符 < > & 时,telegram api 将无法处理导致无法发送消息

## 📇功能

- 添加呼叫 morny 主人功能
- 添加 /ip 和 /whois 命令 (#17 @186526
- 添加 /event_hack 命令
- 添加 /jrrp 命令以及其 xmomi 版本后端
- /user 命令现在在无参数时返回调用者的数据而非回报错误
- /runtime 命令的返回数据的 continuous 字段添加了启动时间回报

## 🔌系统接口

- 添加了主人 id 记录,用作可信认证和部分功能
- 封装对象化了 MornyCoeur 和 MornyTrusted
- 程序启动参数扩充
  - 以选项方式为 token 与 username 字段赋值
  - 实现 morny 主人和信任群组字段的赋值
  - 同时标记旧的 token username 参数赋值可能废弃
- 添加 apache-commons-text 类库 用于进行 HTML 实体转义
- 添加了 escapeHtmlTelegram 工具 用于进行 telegram api HTML 实体转义
- 移除了没有再被使用的 repeatChars 工具

## 🔩技术修改/typo

- 事件的"忽略程序启动前"作用范围现在扩大为所有 Message 和 EditedMessage 事件
- 添加了贴纸管理类
- 修改 /runtime 的文本
- 补充部分 javadoc
- 为项目添加 .editorconfig
- 将部分误导入的 jetbrains.NotNull 改为 javax.Nonnull
This commit is contained in:
A.C.Sukazyo Eyre 2021-12-27 17:05:23 +08:00
commit 31e25308bb
Signed by: Eyre_S
GPG Key ID: EFB47D98FE082FAD
23 changed files with 1408 additions and 109 deletions

532
.editorconfig Normal file
View File

@ -0,0 +1,532 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = true
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = false
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = false
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = true
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = false
ij_java_align_multiline_ternary_operation = true
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = true
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = true
ij_java_array_initializer_right_brace_on_new_line = true
ij_java_array_initializer_wrap = on_every_item
ij_java_assert_statement_colon_on_next_line = true
ij_java_assert_statement_wrap = normal
ij_java_assignment_wrap = normal
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 1
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 0
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 1
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_block_brace_style = end_of_line
ij_java_block_comment_add_space = false
ij_java_block_comment_at_first_column = true
ij_java_builder_methods = none
ij_java_call_parameters_new_line_after_left_paren = true
ij_java_call_parameters_right_paren_on_new_line = true
ij_java_call_parameters_wrap = normal
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 5
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = false
ij_java_doc_align_param_comments = false
ij_java_doc_do_not_wrap_if_one_line = true
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = true
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_entity_dd_suffix = EJB
ij_java_entity_eb_suffix = Bean
ij_java_entity_hi_suffix = Home
ij_java_entity_lhi_prefix = Local
ij_java_entity_lhi_suffix = Home
ij_java_entity_li_prefix = Local
ij_java_entity_pk_class = java.lang.String
ij_java_entity_vo_suffix = VO
ij_java_enum_constants_wrap = on_every_item
ij_java_extends_keyword_wrap = normal
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = normal
ij_java_finally_on_new_line = false
ij_java_for_brace_force = if_multiline
ij_java_for_statement_new_line_after_left_paren = true
ij_java_for_statement_right_paren_on_new_line = true
ij_java_for_statement_wrap = on_every_item
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = *,|,javax.**,java.**,|,$*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = false
ij_java_keep_first_column_comment = false
ij_java_keep_indents_on_empty_lines = true
ij_java_keep_line_breaks = false
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_at_first_column = true
ij_java_message_dd_suffix = EJB
ij_java_message_eb_suffix = Bean
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = normal
ij_java_method_parameters_new_line_after_left_paren = true
ij_java_method_parameters_right_paren_on_new_line = true
ij_java_method_parameters_wrap = normal
ij_java_modifier_list_wrap = false
ij_java_names_count_to_use_import_on_demand = 3
ij_java_new_line_after_lparen_in_record_header = false
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_java_parameter_annotation_wrap = normal
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = true
ij_java_resource_list_right_paren_on_new_line = true
ij_java_resource_list_wrap = normal
ij_java_rparen_on_new_line_in_record_header = false
ij_java_session_dd_suffix = EJB
ij_java_session_eb_suffix = Bean
ij_java_session_hi_suffix = Home
ij_java_session_lhi_prefix = Local
ij_java_session_lhi_suffix = Home
ij_java_session_li_prefix = Local
ij_java_session_si_suffix = Service
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = false
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = true
ij_java_space_before_opening_angle_bracket_in_type_parameter = true
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = on_every_item
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = normal
ij_java_throws_list_wrap = off
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = true
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = on_every_item
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = true
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = true
ij_java_wrap_long_lines = false
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.isc,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_smart_tabs = true
ij_xml_align_attributes = false
ij_xml_align_text = false
ij_xml_attribute_wrap = off
ij_xml_block_comment_add_space = false
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = true
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = true
ij_xml_text_wrap = normal
ij_xml_use_custom_settings = false
[{*.gant,*.groovy,*.gson,*.gy}]
ij_smart_tabs = true
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = true
ij_groovy_array_initializer_new_line_after_left_brace = true
ij_groovy_array_initializer_right_brace_on_new_line = true
ij_groovy_array_initializer_wrap = on_every_item
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 1
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_add_space = false
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = true
ij_groovy_call_parameters_right_paren_on_new_line = true
ij_groovy_call_parameters_wrap = on_every_item
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = true
ij_groovy_enum_constants_wrap = on_every_item
ij_groovy_extends_keyword_wrap = normal
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = true
ij_groovy_for_statement_right_paren_on_new_line = true
ij_groovy_for_statement_wrap = on_every_item
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = true
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = true
ij_groovy_method_parameters_right_paren_on_new_line = true
ij_groovy_method_parameters_wrap = on_every_item
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_groovy_parameter_annotation_wrap = normal
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = true
ij_groovy_resource_list_right_paren_on_new_line = true
ij_groovy_resource_list_wrap = on_every_item
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = false
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = true
ij_groovy_space_before_quest = true
ij_groovy_space_before_record_parentheses = false
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = normal
ij_groovy_throws_keyword_wrap = normal
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = normal
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_chain_calls_after_dot = false
ij_groovy_wrap_long_lines = false
[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
ij_smart_tabs = true
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = false
ij_html_align_text = false
ij_html_attribute_wrap = off
ij_html_block_comment_add_space = false
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = none
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 0
ij_html_keep_indents_on_empty_lines = true
ij_html_keep_line_breaks = false
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = true
ij_html_text_wrap = off
[{*.markdown,*.md}]
ij_smart_tabs = true
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_keep_indents_on_empty_lines = true
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
[{*.properties,spring.handlers,spring.schemas}]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = true
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = true

View File

@ -1,10 +1,10 @@
## Core ## Core
VERSION = 0.4.1.3 VERSION = 0.4.2.11
# dependencies # dependencies
libSpotbugsVersion = 4.5.0 libSpotbugsVersion = 4.5.2
libMessivaVersion = 0.1.0.1 libMessivaVersion = 0.1.0.1

View File

@ -4,6 +4,6 @@ package cc.sukazyo.cono.morny;
* the final field that will be updated by gradle automatically. * the final field that will be updated by gradle automatically.
*/ */
public class GradleProjectConfigures { public class GradleProjectConfigures {
public static final String VERSION = "0.4.1.3"; public static final String VERSION = "0.4.2.11";
public static final long COMPILE_TIMESTAMP = 1639476313268L; public static final long COMPILE_TIMESTAMP = 1640595623685L;
} }

View File

@ -17,8 +17,14 @@ import static cc.sukazyo.cono.morny.Log.logger;
*/ */
public class MornyCoeur { public class MornyCoeur {
/** 当前程序的 Morny Coeur 实例 */
private static MornyCoeur INSTANCE;
/** 当前 Morny 的{@link MornyTrusted 信任验证机}实例 */
private final MornyTrusted trusted;
/** morny 的 bot 账户 */ /** morny 的 bot 账户 */
private static TelegramBot account; private final TelegramBot account;
/** /**
* morny bot 账户的用户名<br> * morny bot 账户的用户名<br>
* <br> * <br>
@ -28,36 +34,40 @@ public class MornyCoeur {
* 如果在登陆之前就定义了此字段则登陆代码会验证登陆的 bot username * 如果在登陆之前就定义了此字段则登陆代码会验证登陆的 bot username
* 是否与定义的 username 符合如果不符合则会报错 * 是否与定义的 username 符合如果不符合则会报错
*/ */
private static String username; private final String username;
/** /**
* morny 的事件忽略前缀时间<br> * morny 的事件忽略前缀时间<br>
* <br> * <br>
* {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock} * {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock}
* 会根据这里定义的时间戳取消掉比此时间更早的事件链 * 会根据这里定义的时间戳取消掉比此时间更早的事件链
*/ */
public static long latestEventTimestamp; public long latestEventTimestamp;
/** /**
* morny 主程序启动时间<br> * morny 主程序启动时间<br>
* 用于统计数据 * 用于统计数据
*/ */
public static final long coeurStartTimestamp = System.currentTimeMillis(); public static final long coeurStartTimestamp = System.currentTimeMillis();
private record LogInResult(TelegramBot account, String username) { }
/** /**
* bot 启动入口执行 bot 初始化 * 执行 bot 初始化
* *
* @param botKey bot telegram bot api token * @param botKey bot telegram bot api token
* @param botUsername bot username 限定如果为 null 则表示不限定 * @param botUsername bot username 限定如果为 null 则表示不限定
* 如果指定则登录时会检查所登陆的 bot 的用户名是否与此相等 * 如果指定则登录时会检查所登陆的 bot 的用户名是否与此相等
* @param master morny 实例所信任的主人的 id用于初始化 {@link #trusted}
* @param trustedChat morny 实例所信任的群组的 id用于初始化 {@link #trusted}
* @param latestEventTimestamp 事件处理器会处理事件的最早时间戳 * @param latestEventTimestamp 事件处理器会处理事件的最早时间戳
* 只有限定的 message 事件会受此影响 * 只有限定的 message 事件会受此影响
* 单位为毫秒 * 单位为毫秒
*/ */
public static void main (@Nonnull String botKey, @Nullable String botUsername, long latestEventTimestamp) { private MornyCoeur (
@Nonnull String botKey, @Nullable String botUsername,
MornyCoeur.latestEventTimestamp = latestEventTimestamp; long master, long trustedChat, long latestEventTimestamp
) {
logger.info("System Starting");
this.latestEventTimestamp = latestEventTimestamp;
configureSafeExit(); configureSafeExit();
logger.info("args key:\n " + botKey); logger.info("args key:\n " + botKey);
@ -65,23 +75,57 @@ public class MornyCoeur {
logger.info("login as:\n " + botUsername); logger.info("login as:\n " + botUsername);
} }
try { account = login(botKey); } try {
catch (Exception e) { logger.error("Cannot login to bot/api. :\n " + e.getMessage()); System.exit(-1); } final LogInResult loginResult = login(botKey);
this.account = loginResult.account;
this.username = loginResult.username;
this.trusted = new MornyTrusted(master, trustedChat);
logger.info(String.format("""
trusted param set:
- master (id)
%d
- trusted chat (id)
%d""",
master, trustedChat
));
}
catch (Exception e) {
RuntimeException ex = new RuntimeException("Cannot login to bot/api. :\n " + e.getMessage());
logger.error(ex.getMessage());
throw ex;
}
logger.info("Bot login succeed."); logger.info("Bot login succeed.");
TrackerDataManager.init(); }
EventListeners.registerAllListeners();
account.setUpdatesListener(OnUpdate::onNormalUpdate); /**
* 向外界暴露的 morny 初始化入口.
logger.info("System start complete"); * <p>
* 如果 morny 已经初始化则不会进行初始化抛出错误消息并直接退出方法
*
* @see #MornyCoeur 程序初始化方法
*/
public static void main (
@Nonnull String botKey, @Nullable String botUsername,
long master, long trustedChat, long latestEventTimestamp
) {
if (INSTANCE == null) {
logger.info("System Starting");
INSTANCE = new MornyCoeur(botKey, botUsername, master, trustedChat, latestEventTimestamp);
TrackerDataManager.init();
EventListeners.registerAllListeners();
INSTANCE.account.setUpdatesListener(OnUpdate::onNormalUpdate);
logger.info("System start complete");
return;
}
logger.error("System already started coeur!!!");
} }
/** /**
* 用于退出时进行缓存的任务处理等进行安全退出 * 用于退出时进行缓存的任务处理等进行安全退出
*/ */
private static void exitCleanup () { private void exitCleanup () {
TrackerDataManager.DAEMON.interrupt(); TrackerDataManager.DAEMON.interrupt();
TrackerDataManager.trackingLock.lock(); TrackerDataManager.trackingLock.lock();
} }
@ -89,8 +133,8 @@ public class MornyCoeur {
/** /**
* 为程序在虚拟机上添加退出钩子 * 为程序在虚拟机上添加退出钩子
*/ */
private static void configureSafeExit () { private void configureSafeExit () {
Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup)); Runtime.getRuntime().addShutdownHook(new Thread(this::exitCleanup));
} }
/** /**
@ -104,18 +148,17 @@ public class MornyCoeur {
* @return 成功登录后的 {@link TelegramBot} 对象 * @return 成功登录后的 {@link TelegramBot} 对象
*/ */
@Nonnull @Nonnull
public static TelegramBot login (@Nonnull String key) { private LogInResult login (@Nonnull String key) {
final TelegramBot account = new TelegramBot(key); final TelegramBot account = new TelegramBot(key);
logger.info("Trying to login..."); logger.info("Trying to login...");
for (int i = 1; i < 4; i++) { for (int i = 1; i < 4; i++) {
if (i != 1) logger.info("retrying..."); if (i != 1) logger.info("retrying...");
try { try {
final String username = account.execute(new GetMe()).user().username(); final String username = account.execute(new GetMe()).user().username();
if (MornyCoeur.username != null && !MornyCoeur.username.equals(username)) if (this.username != null && !this.username.equals(username))
throw new RuntimeException("Required the bot @" + MornyCoeur.username + " but @" + username + " logged in!"); throw new RuntimeException("Required the bot @" + this.username + " but @" + username + " logged in!");
else MornyCoeur.username = username;
logger.info("Succeed login to @" + username); logger.info("Succeed login to @" + username);
return account; return new LogInResult(account, username);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(System.out); e.printStackTrace(System.out);
logger.error("login failed."); logger.error("login failed.");
@ -129,8 +172,9 @@ public class MornyCoeur {
* *
* @return {@link #account MornyCoeur.account} * @return {@link #account MornyCoeur.account}
*/ */
@Nonnull
public static TelegramBot getAccount () { public static TelegramBot getAccount () {
return account; return INSTANCE.account;
} }
/** /**
@ -138,8 +182,29 @@ public class MornyCoeur {
* *
* @return {@link #username MornyCoeur.username} * @return {@link #username MornyCoeur.username}
*/ */
@Nonnull
public static String getUsername () { public static String getUsername () {
return username; return INSTANCE.username;
}
/**
*
* 获取忽略时间点
*
* @return {@link #latestEventTimestamp MornyCoeur.latestEventTimestamp}
*/
public static long getLatestEventTimestamp () {
return INSTANCE.latestEventTimestamp;
}
/**
* 获取 Morny {@link MornyTrusted 信任验证机}
*
* @return {@link #trusted MornyCoeur.trusted}
*/
@Nonnull
public static MornyTrusted trustedInstance () {
return INSTANCE.trusted;
} }
} }

View File

@ -12,7 +12,18 @@ public class MornyTrusted {
* 群聊id其指向的群聊指示了哪个群的成员是受信任的 * 群聊id其指向的群聊指示了哪个群的成员是受信任的
* @see #isTrusted(long) 受信检查 * @see #isTrusted(long) 受信检查
*/ */
public static final long TRUSTED_CHAT_ID = -1001541451710L; public final Long TRUSTED_CHAT_ID;
/**
* morny 的主人<br>
* 这项值的对象总是会被认为是可信任的
*/
public final long MASTER;
public MornyTrusted (long master, long trustedChatId) {
this.TRUSTED_CHAT_ID = trustedChatId;
this.MASTER = master;
}
/** /**
* 用于检查一个 telegram-user 是否受信任<br> * 用于检查一个 telegram-user 是否受信任<br>
@ -24,7 +35,8 @@ public class MornyTrusted {
* @param userId 需要检查的用户的id * @param userId 需要检查的用户的id
* @return 所传递的用户id对应的用户是否受信任 * @return 所传递的用户id对应的用户是否受信任
*/ */
public static boolean isTrusted (long userId) { public boolean isTrusted (long userId) {
if (userId == MASTER) return true;
final ChatMember chatMember = MornyCoeur.getAccount().execute(new GetChatMember(TRUSTED_CHAT_ID, userId)).chatMember(); final ChatMember chatMember = MornyCoeur.getAccount().execute(new GetChatMember(TRUSTED_CHAT_ID, userId)).chatMember();
return ( return (
chatMember != null && ( chatMember != null && (

View File

@ -35,6 +35,13 @@ public class ServerMain {
* 不要同时使用 {@code --no-hello}原因见下 * 不要同时使用 {@code --no-hello}原因见下
* </li> * </li>
* <li> * <li>
* {@code --token} <b>主程序模式的必选项</b><br>
* 用于 bot 启动的 telegram bot api token
* </li>
* <li>
* {@code --username} {@link MornyCoeur#getUsername() bot username} 预定义
* </li>
* <li>
* {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画 * {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画
* {@code --only-hello} 参数不兼容 会导致程序完全没有任何输出 * {@code --only-hello} 参数不兼容 会导致程序完全没有任何输出
* </li> * </li>
@ -44,9 +51,15 @@ public class ServerMain {
* </li> * </li>
* </ul> * </ul>
* 除去选项之外第一个参数会被赋值为 bot telegram bot api token * 除去选项之外第一个参数会被赋值为 bot telegram bot api token
* 第二个参数会被赋值为 bot username 限定名其余的参数会被认定为无法理解 * 第二个参数会被赋值为 bot username 限定名其余的参数会被认定为无法理解<br>
* <br>
* <b> {@code 0.4.2.3}token username 的赋值已被选项组支持</b><br>
* 使用参数所进行取值的 token username 已被转移至 {@code --token} {@code --username} 参数
* <u>或许直接参数赋值的支持将计划在 {@code 0.4.3} 标记废弃并在 {@code 0.5} 删除</u>
* <s>但实际上这并不影响现在的使用选项赋值目前仍属于测试功能</s><br>
* <b>但请勿混用</b>这将使两个赋值出现混淆并<b>产生不可知的结果</b>
* *
* @see MornyCoeur#main(String, String, long) * @see MornyCoeur#main(String, String, long, long, long)
* @since 0.4.0.0 * @since 0.4.0.0
* @param args 参数组 * @param args 参数组
*/ */
@ -55,12 +68,14 @@ public class ServerMain {
String key = null; String key = null;
String username = null; String username = null;
boolean outdatedBlock = false; boolean outdatedBlock = false;
long master = 793274677L;
long trustedChat = -1001541451710L;
for (String arg : args) { for (int i = 0; i < args.length; i++) {
if (arg.startsWith("-")) { if (args[i].startsWith("-")) {
switch (arg) { switch (args[i]) {
case "--outdated-block" -> { case "--outdated-block" -> {
outdatedBlock = true; outdatedBlock = true;
continue; continue;
@ -77,22 +92,42 @@ public class ServerMain {
versionEchoMode = true; versionEchoMode = true;
continue; continue;
} }
case "--token" -> {
i++;
key = args[i];
continue;
}
case "--username" -> {
i++;
username = args[i];
continue;
}
case "--master" -> {
i++;
master = Long.parseLong(args[i]);
continue;
}
case "--trusted-chat" -> {
i++;
trustedChat = Long.parseLong(args[i]);
continue;
}
} }
} else { } else {
if (key == null) { if (key == null) {
key = arg; key = args[i];
continue; continue;
} }
if (username == null) { if (username == null) {
username = arg; username = args[i];
continue; continue;
} }
} }
logger.warn("Can't understand arg to some meaning :\n " + arg); logger.warn("Can't understand arg to some meaning :\n " + args[i]);
} }
@ -120,7 +155,7 @@ public class ServerMain {
if (welcomeEchoMode) return; if (welcomeEchoMode) return;
assert key != null; assert key != null;
MornyCoeur.main(key, username, outdatedBlock?System.currentTimeMillis():0); MornyCoeur.main(key, username, master, trustedChat, outdatedBlock?System.currentTimeMillis():0);
} }

View File

@ -9,6 +9,8 @@ public class EventListeners {
public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction(); public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction();
public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock(); public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock();
public static final OnInlineQuery INLINE_QUERY = new OnInlineQuery(); public static final OnInlineQuery INLINE_QUERY = new OnInlineQuery();
public static final OnCallMe CALL_ME = new OnCallMe();
public static final OnEventHackHandle EVENT_HACK_HANDLE = new OnEventHackHandle();
public static void registerAllListeners () { public static void registerAllListeners () {
EventListenerManager.addListener( EventListenerManager.addListener(
@ -16,7 +18,9 @@ public class EventListeners {
UPDATE_TIMESTAMP_OFFSET_LOCK, UPDATE_TIMESTAMP_OFFSET_LOCK,
COMMANDS_LISTENER, COMMANDS_LISTENER,
USER_SLASH_ACTION, USER_SLASH_ACTION,
INLINE_QUERY INLINE_QUERY,
CALL_ME,
EVENT_HACK_HANDLE
); );
} }

View File

@ -0,0 +1,139 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.ForwardMessage;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.util.StringUtils.escapeHtmlTelegram;
/**
* 通过 bot 呼叫主人的事件监听管理类
* @since 0.4.2.1
*/
public class OnCallMe extends EventListener {
/**
* 主人的 telegram user id同时被用于 chat id<br>
* 跟随 {@link MornyTrusted#MASTER} 的值
* @since 0.4.2.1
*/
private static final long ME = MornyCoeur.trustedInstance().MASTER;
/**
* 监听私聊 bot 的消息进行呼叫关键字匹配
* 如果成功将会执行呼叫函数并向呼叫者回显{@link TelegramStickers#ID_WAITING "已呼叫"贴纸}
*
* @param update 事件基础参数消息事件所属的 tgapi:update 对象
* @return 事件基础返回值是否已完成处理事件<br>
* 如果匹配到呼叫则返回{@code true}反之返回{@code false}
*/
@Override
public boolean onMessage (@Nonnull Update update) {
if (update.message().text() == null)
return false;
if (update.message().chat().type() != Chat.Type.Private)
return false;
switch (update.message().text().toLowerCase()) {
case "steam", "sbeam", "sdeam" ->
requestSteamJoin(update);
case "hana paresu", "花宫", "内群" ->
requestHanaParesuJoin(update);
default -> {
if (update.message().text().startsWith("cc::")) {
requestCustomCall(update);
break;
}
return false;
}
}
MornyCoeur.getAccount().execute(new SendSticker(
update.message().chat().id(),
TelegramStickers.ID_SENT
).replyToMessageId(update.message().messageId())
);
return true;
}
/**
* 执行 steam library 呼叫<br>
* 将会向 {@link #ME} 发送
*
* @param event 执行呼叫的tg事件
*/
private static void requestSteamJoin (Update event) {
MornyCoeur.getAccount().execute(new SendMessage(
ME, String.format(
"""
request <b>STEAM LIBRARY</b>
from <a href="tg://user?id=%d">%s</a>""",
event.message().from().id(),
escapeHtmlTelegram(
event.message().from().firstName() + " " + event.message().from().lastName()
)
)
).parseMode(ParseMode.HTML));
}
/**
* 执行花宫呼叫<br>
* 将会向 {@link #ME} 发送
*
* @param event 执行呼叫的tg事件
*/
private static void requestHanaParesuJoin (Update event) {
MornyCoeur.getAccount().execute(new SendMessage(
ME, String.format(
"""
request <b>Hana Paresu</b>
from <a href="tg://user?id=%d">%s</a>""",
event.message().from().id(),
escapeHtmlTelegram(
event.message().from().firstName() + " " + event.message().from().lastName()
)
)
).parseMode(ParseMode.HTML));
}
/**
* 执行自定义呼叫<br>
* 将会向 {@link #ME} 发送一个 request 数据消息和转发的原始请求消息<br>
* <br>
* <u>known issue</u><ul>
* <li>无法处理与转发带有媒体的消息</li>
* </ul>
* <br>
* 现在你可以通过这个 bot 来呼叫主人sukazyo任何事情了
* <s>但是直接私聊sukazyo不好吗</s>
*
* @param event 执行呼叫的tg事件
* @since 0.4.2.2
*/
private static void requestCustomCall (Update event) {
MornyCoeur.getAccount().execute(new SendMessage(
ME, String.format(
"""
request <u>[???]</u>
from <a href="tg://user?id=%d">%s</a>""",
event.message().from().id(),
escapeHtmlTelegram(
event.message().from().firstName() + " " + event.message().from().lastName()
)
)
).parseMode(ParseMode.HTML));
MornyCoeur.getAccount().execute(new ForwardMessage(
ME,
event.message().chat().id(),
event.message().messageId()
));
}
}

View File

@ -3,11 +3,13 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.GradleProjectConfigures; import cc.sukazyo.cono.morny.GradleProjectConfigures;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornySystem; import cc.sukazyo.cono.morny.MornySystem;
import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.bot.api.InputCommand; import cc.sukazyo.cono.morny.bot.api.InputCommand;
import cc.sukazyo.cono.morny.bot.event.on_commands.EventHack;
import cc.sukazyo.cono.morny.bot.event.on_commands.GetUsernameAndId; import cc.sukazyo.cono.morny.bot.event.on_commands.GetUsernameAndId;
import cc.sukazyo.cono.morny.util.CommonFormatUtils; import cc.sukazyo.cono.morny.bot.event.on_commands.Ip186Query;
import cc.sukazyo.cono.morny.data.MornyJrrp;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
@ -16,15 +18,12 @@ import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.CommonFormatUtils.formatDate;
import static cc.sukazyo.cono.morny.util.CommonFormatUtils.formatDuration;
import static cc.sukazyo.cono.morny.util.StringUtils.escapeHtmlTelegram;
public class OnCommandExecute extends EventListener { public class OnCommandExecute extends EventListener {
private static final String ONLINE_STATUS_RETURN_STICKER_ID = "CAACAgEAAx0CW-CvvgAC5eBhhhODGRuu0pxKLwoQ3yMsowjviAACcycAAnj8xgVVU666si1utiIE";
private static final String HELLO_STICKER_ID = "CAACAgEAAxkBAAMnYYYWKNXO4ibo9dlsmDctHhhV6fIAAqooAAJ4_MYFJJhrHS74xUAiBA";
private static final String EXIT_STICKER_ID = "CAACAgEAAxkBAAMoYYYWt8UjvP0N405SAyvg2SQZmokAAkMiAAJ4_MYFw6yZLu06b-MiBA";
private static final String EXIT_403_STICKER_ID = "CAACAgEAAxkBAAMqYYYa_7hpXH6hMOYMX4Nh8AVYd74AAnQnAAJ4_MYFRdmmsQKLDZgiBA";
private static final String NON_COMMAND_QUESTION_STICKER_ID = "CAACAgEAAx0CSQh32gABA966YbRJpbmi2lCHINBDuo1DknSTsbsAAqUoAAJ4_MYFUa8SIaZriAojBA";
@Override @Override
public boolean onMessage (@Nonnull Update event) { public boolean onMessage (@Nonnull Update event) {
if (event.message().text() == null) { if (event.message().text() == null) {
@ -38,6 +37,9 @@ public class OnCommandExecute extends EventListener {
case "/user": case "/user":
GetUsernameAndId.exec(command.getArgs(), event); GetUsernameAndId.exec(command.getArgs(), event);
break; break;
case "/event_hack":
EventHack.exec(event, command);
break;
case "/o": case "/o":
onCommandOnExec(event); onCommandOnExec(event);
break; break;
@ -54,6 +56,13 @@ public class OnCommandExecute extends EventListener {
case "/runtime": case "/runtime":
onCommandRuntimeExec(event); onCommandRuntimeExec(event);
break; break;
case "/jrrp":
onCommandJrrpExec(event);
break;
case "/ip":
case "/whois":
Ip186Query.exec(event, command);
break;
default: default:
return nonCommandExecutable(event, command); return nonCommandExecutable(event, command);
} }
@ -65,7 +74,7 @@ public class OnCommandExecute extends EventListener {
else { // 无法解析的显式命令格式报错找不到命令 else { // 无法解析的显式命令格式报错找不到命令
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
NON_COMMAND_QUESTION_STICKER_ID TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
return true; return true;
@ -75,24 +84,24 @@ public class OnCommandExecute extends EventListener {
private void onCommandOnExec (@Nonnull Update event) { private void onCommandOnExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
ONLINE_STATUS_RETURN_STICKER_ID TelegramStickers.ID_ONLINE_STATUS_RETURN
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
} }
private void onCommandHelloExec (@Nonnull Update event) { private void onCommandHelloExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
HELLO_STICKER_ID TelegramStickers.ID_HELLO
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
} }
private void onCommandExitExec (@Nonnull Update event) { private void onCommandExitExec (@Nonnull Update event) {
if (MornyTrusted.isTrusted(event.message().from().id())) { if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
EXIT_STICKER_ID TelegramStickers.ID_EXIT
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
logger.info("Morny exited by user @" + event.message().from().username()); logger.info("Morny exited by user @" + event.message().from().username());
@ -100,7 +109,7 @@ public class OnCommandExecute extends EventListener {
} else { } else {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
EXIT_403_STICKER_ID TelegramStickers.ID_403
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
logger.info("403 exited tag from user @" + event.message().from().username()); logger.info("403 exited tag from user @" + event.message().from().username());
@ -110,7 +119,8 @@ public class OnCommandExecute extends EventListener {
private void onCommandVersionExec (@Nonnull Update event) { private void onCommandVersionExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendMessage( MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(), event.message().chat().id(),
String.format(""" String.format(
"""
version: version:
- <code>%s</code> - <code>%s</code>
core md5_hash: core md5_hash:
@ -118,14 +128,17 @@ public class OnCommandExecute extends EventListener {
compile timestamp: compile timestamp:
- <code>%d</code> - <code>%d</code>
- <code>%s [UTC]</code>""", - <code>%s [UTC]</code>""",
MornySystem.VERSION, escapeHtmlTelegram(MornySystem.VERSION),
MornySystem.getJarMd5(), escapeHtmlTelegram(MornySystem.getJarMd5()),
GradleProjectConfigures.COMPILE_TIMESTAMP, GradleProjectConfigures.COMPILE_TIMESTAMP,
CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0) escapeHtmlTelegram(formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0))
) )
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
} }
/**
* @since 0.4.1.2
*/
private void onCommandRuntimeExec (@Nonnull Update event) { private void onCommandRuntimeExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendMessage( MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(), event.message().chat().id(),
@ -137,34 +150,52 @@ public class OnCommandExecute extends EventListener {
java runtime: java runtime:
- <code>%s</code> - <code>%s</code>
- <code>%s</code> - <code>%s</code>
memory: vm memory:
- <code>%d</code> / <code>%d</code> MB - <code>%d</code> / <code>%d</code> MB
morny version: morny version:
- <code>%s</code> - <code>%s</code>
- <code>%s</code> - <code>%s</code>
- <code>%s [UTC]</code> - <code>%s [UTC]</code>
- [<code>%d</code>] - [<code>%d</code>]
continuous continuous:
- <code>%s</code> - <code>%s</code>
- [<code>%d</code>]
- <code>%s [UTC]</code>
- [<code>%d</code>]""", - [<code>%d</code>]""",
// system // system
System.getProperty("os.name"), escapeHtmlTelegram(System.getProperty("os.name")),
System.getProperty("os.version"), escapeHtmlTelegram(System.getProperty("os.version")),
Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(),
// java // java
System.getProperty("java.vm.name"), escapeHtmlTelegram(System.getProperty("java.vm.name")),
System.getProperty("java.version"), escapeHtmlTelegram(System.getProperty("java.version")),
// memory // memory
Runtime.getRuntime().totalMemory() / 1024 / 1024, Runtime.getRuntime().totalMemory() / 1024 / 1024,
Runtime.getRuntime().maxMemory() / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024,
// version // version
MornySystem.VERSION, escapeHtmlTelegram(MornySystem.VERSION),
MornySystem.getJarMd5(), escapeHtmlTelegram(MornySystem.getJarMd5()),
CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0), escapeHtmlTelegram(formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)),
GradleProjectConfigures.COMPILE_TIMESTAMP, GradleProjectConfigures.COMPILE_TIMESTAMP,
// continuous // continuous
CommonFormatUtils.formatDuration(System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp), escapeHtmlTelegram(formatDuration(System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp)),
System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp,
escapeHtmlTelegram(formatDate(MornyCoeur.coeurStartTimestamp, 0)),
MornyCoeur.coeurStartTimestamp
)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
private void onCommandJrrpExec (Update event) {
final double jrrp = MornyJrrp.getJrrpFromTelegramUser(event.message().from(), System.currentTimeMillis());
final String endChar = jrrp>70 ? "!" : jrrp>30 ? ";" : "...";
MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
String.format(
"<a href='tg://user?id=%d'>%s</a> 在(utc的)今天的运气指数是———— <code>%.2f%%</code> %s",
event.message().from().id(),
escapeHtmlTelegram(event.message().from().firstName()),
jrrp, escapeHtmlTelegram(endChar)
) )
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
} }

View File

@ -0,0 +1,144 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.util.StringUtils;
import com.google.gson.GsonBuilder;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import static cc.sukazyo.cono.morny.Log.logger;
/**
* 事件劫持与序列化工具.
* @since 0.4.2.0
*/
public class OnEventHackHandle extends EventListener {
/** 事件劫持请求列表 */
private static final Map<String, Hacker> hackers = new HashMap<>();
/**
* 触发事件劫持的限定条件.
* @since 0.4.2.0
*/
public enum HackType {
/** 只有相同用户发起的事件才会被触发 */
USER,
/** 只有相同群组内发生的事件才会触发 */
GROUP,
/** 任何事件都可以触发 */
ANY
}
public record Hacker(long fromChatId, long fromMessageId) {
@Override public String toString() {
return fromChatId + "/" + fromMessageId;
}
}
/**
* @since 0.4.2.0
*/
public static void registerHack(long fromMessageId, long fromUserId, long fromChatId, @Nonnull HackType type) {
String rec = null;
switch (type) {
case USER -> rec = String.format("((%d))", fromUserId);
case GROUP -> rec = String.format("{{%d}}", fromChatId);
case ANY -> rec = "[[]]";
}
hackers.put(rec, new Hacker(fromChatId, fromMessageId));
logger.debug("add hacker track " + rec);
}
private boolean onEventHacked (Update update, long chatId, long fromUser) {
logger.debug(String.format("try hack {{%d}}((%d))", chatId, fromUser));
Hacker x;
x = hackers.remove(String.format("((%d))", fromUser));
if (x == null) x = hackers.remove(String.format("{{%d}}", chatId));
if (x == null) x = hackers.remove("[[]]");
if (x == null) return false;
logger.debug("hacked event by " + x);
MornyCoeur.getAccount().execute(new SendMessage(x.fromChatId, String.format(
"<code>%s</code>",
StringUtils.escapeHtmlTelegram(new GsonBuilder().setPrettyPrinting().create().toJson(update))
)).parseMode(ParseMode.HTML).replyToMessageId((int)x.fromMessageId));
return true;
}
@Override
public boolean onMessage (@Nonnull Update update) {
return onEventHacked(update, update.message().chat().id(), update.message().from().id());
}
@Override
public boolean onEditedMessage (@Nonnull Update update) {
return onEventHacked(update, update.editedMessage().chat().id(), update.editedMessage().from().id());
}
@Override
public boolean onChannelPost (@Nonnull Update update) {
return onEventHacked(update, update.channelPost().chat().id(), update.channelPost().from().id());
}
@Override
public boolean onEditedChannelPost (@Nonnull Update update) {
return onEventHacked(update, update.editedChannelPost().chat().id(), update.editedChannelPost().from().id());
}
@Override
public boolean onInlineQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.inlineQuery().from().id());
}
@Override
public boolean onChosenInlineResult (@Nonnull Update update) {
return onEventHacked(update, 0, update.chosenInlineResult().from().id());
}
@Override
public boolean onCallbackQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.callbackQuery().from().id());
}
@Override
public boolean onShippingQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.shippingQuery().from().id());
}
@Override
public boolean onPreCheckoutQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.preCheckoutQuery().from().id());
}
@Override
public boolean onPoll (@Nonnull Update update) {
return onEventHacked(update, 0, 0);
}
@Override
public boolean onPollAnswer (@Nonnull Update update) {
return onEventHacked(update, 0, update.pollAnswer().user().id());
}
@Override
public boolean onMyChatMemberUpdated (@Nonnull Update update) {
return onEventHacked(update, update.myChatMember().chat().id(), update.myChatMember().from().id());
}
@Override
public boolean onChatMemberUpdated (@Nonnull Update update) {
return onEventHacked(update, update.chatMember().chat().id(), update.chatMember().from().id());
}
@Override
public boolean onChatJoinRequest (@Nonnull Update update) {
return onEventHacked(update, update.chatJoinRequest().chat().id(), update.chatJoinRequest().from().id());
}
}

View File

@ -8,9 +8,13 @@ import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent; import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.AnswerInlineQuery; import com.pengrad.telegrambot.request.AnswerInlineQuery;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
/** /**
* telegram inlineQuery 功能的处理类
* 也是一个 InlineQueryManager还没做
*
* @since 0.4.1.3 * @since 0.4.1.3
*/ */
public class OnInlineQuery extends EventListener { public class OnInlineQuery extends EventListener {
@ -19,7 +23,7 @@ public class OnInlineQuery extends EventListener {
* @since 0.4.1.3 * @since 0.4.1.3
*/ */
@Override @Override
public boolean onInlineQuery (@NotNull Update update) { public boolean onInlineQuery (@Nonnull Update update) {
MornyCoeur.getAccount().execute(new AnswerInlineQuery(update.inlineQuery().id(), new InlineQueryResultArticle[]{ MornyCoeur.getAccount().execute(new AnswerInlineQuery(update.inlineQuery().id(), new InlineQueryResultArticle[]{
new InlineQueryResultArticle( new InlineQueryResultArticle(
EncryptUtils.encryptByMD5(update.inlineQuery().query()), EncryptUtils.encryptByMD5(update.inlineQuery().query()),

View File

@ -3,13 +3,54 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
/**
* 阻止 {@link MornyCoeur#latestEventTimestamp 指定时间} 之前的事件处理.
* <p>
* 只支持以下事件
* <ul>
* <li>{@link EventListener#onMessage(Update) 收到消息}</li>
* <li>{@link EventListener#onEditedMessage(Update) 消息被更新}</li>
* <li>{@link EventListener#onChannelPost(Update) 收到频道消息}</li>
* <li>{@link EventListener#onEditedChannelPost(Update) 频道消息被更新}</li>
* </ul>
* @see #isOutdated 时间判断
*/
public class OnUpdateTimestampOffsetLock extends EventListener { public class OnUpdateTimestampOffsetLock extends EventListener {
/**
* 检查传入时间是否在要求时间之前"过期".
* @param timestamp 传入时间秒级
* @return 如果传入时间在要求时间<u>之前</u>返回true反之false
* @since 0.4.2.7
*/
public boolean isOutdated(long timestamp) {
return timestamp < MornyCoeur.getLatestEventTimestamp()/1000;
}
@Override @Override
public boolean onMessage (@NotNull Update update) { public boolean onMessage (@Nonnull Update update) {
return update.message().date() < MornyCoeur.latestEventTimestamp/1000; return isOutdated(update.message().date());
}
/** @since 0.4.2.6 */
@Override
public boolean onEditedMessage (@Nonnull Update update) {
return isOutdated(update.editedMessage().editDate());
}
/** @since 0.4.2.6 */
@Override
public boolean onChannelPost (@Nonnull Update update) {
return isOutdated(update.channelPost().date());
}
/** @since 0.4.2.6 */
@Override
public boolean onEditedChannelPost (@Nonnull Update update) {
return isOutdated(update.editedChannelPost().editDate());
} }
} }

View File

@ -10,6 +10,8 @@ import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.util.StringUtils.escapeHtmlTelegram;
public class OnUserSlashAction extends EventListener { public class OnUserSlashAction extends EventListener {
@Override @Override
@ -48,11 +50,11 @@ public class OnUserSlashAction extends EventListener {
event.message().chat().id(), event.message().chat().id(),
String.format( String.format(
"<a href='tg://user?id=%d'>%s</a> %s%s <a href='tg://user?id=%d'>%s</a>%s%s", "<a href='tg://user?id=%d'>%s</a> %s%s <a href='tg://user?id=%d'>%s</a>%s%s",
origin.id(), origin.firstName(), origin.id(), escapeHtmlTelegram(origin.firstName()),
verb, (useVerbSuffix?"":""), verb, escapeHtmlTelegram((useVerbSuffix?"":"")),
target.id(), (origin==target ? "自己" : target.firstName()), target.id(), escapeHtmlTelegram((origin==target ? "自己" : target.firstName())),
(hasObject ? (useObjectPrefix ?"": " ") : ""), escapeHtmlTelegram((hasObject ? (useObjectPrefix ?"": " ") : "")),
(hasObject ? object : "") escapeHtmlTelegram((hasObject ? object : ""))
) )
).parseMode(ParseMode.HTML)); ).parseMode(ParseMode.HTML));

View File

@ -0,0 +1,82 @@
package cc.sukazyo.cono.morny.bot.event.on_commands;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.api.InputCommand;
import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendSticker;
/**
* {@link OnEventHackHandle} 的命令行前端
* @since 0.4.2.0
*/
public class EventHack {
/**
* {@link OnEventHackHandle} 的命令行前端<br>
* <br>
* 实现了通过命令行进行 EventHack 功能<br>
* 支持三种模式默认为 {@link OnEventHackHandle.HackType#USER USER}
* {@link OnEventHackHandle.HackType#ANY ANY} 将会通过 {@link MornyTrusted#isTrusted(long)} 检查触发用户的权限
*
* @param event 命令基础参数触发的事件对象本身
* @param command 命令基础参数解析出的命令对象
* @since 0.4.2.0
*/
public static void exec (Update event, InputCommand command) {
boolean isOk = false;
String x_mode = "";
if (command.hasArgs()) {
x_mode = command.getArgs()[0];
}
switch (x_mode) {
case "any":
if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.ANY
);isOk = true;
}
break;
case "group":
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.GROUP
);isOk = true;
break;
default:
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.USER
);isOk = true;
break;
}
if (isOk) {
MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_WAITING
).replyToMessageId(event.message().messageId())
);
} else {
MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId())
);
}
}
}

View File

@ -10,6 +10,8 @@ import com.pengrad.telegrambot.response.GetChatMemberResponse;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.util.StringUtils.escapeHtmlTelegram;
public class GetUsernameAndId { public class GetUsernameAndId {
public static void exec (@Nonnull String[] args, @Nonnull Update event) { public static void exec (@Nonnull String[] args, @Nonnull Update event) {
@ -19,7 +21,7 @@ public class GetUsernameAndId {
"[Unavailable] Too much arguments." "[Unavailable] Too much arguments."
).replyToMessageId(event.message().messageId())); return; } ).replyToMessageId(event.message().messageId())); return; }
long userId = 0; long userId = event.message().from().id();
if ( event.message().replyToMessage()!= null) { if ( event.message().replyToMessage()!= null) {
userId = event.message().replyToMessage().from().id(); userId = event.message().replyToMessage().from().id();
@ -36,14 +38,6 @@ public class GetUsernameAndId {
} }
} }
if (userId == 0) {
MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
"[Unavailable] no userid given."
).replyToMessageId(event.message().messageId()));
return;
}
final GetChatMemberResponse response = MornyCoeur.getAccount().execute( final GetChatMemberResponse response = MornyCoeur.getAccount().execute(
new GetChatMember(event.message().chat().id(), userId) new GetChatMember(event.message().chat().id(), userId)
); );
@ -62,11 +56,20 @@ public class GetUsernameAndId {
userInformation.append(String.format( userInformation.append(String.format(
""" """
userid : userid :
- <code>%d</code> - <code>%d</code>""",
username : userId
- <code>%s</code>""",
userId, user.username()
)); ));
if (user.username() == null) {
userInformation.append("\nusername : <u>null</u>");
} else {
userInformation.append(String.format(
"""
username :
- <code>%s</code>""",
escapeHtmlTelegram(user.username())
));
}
if (user.firstName() == null) { if (user.firstName() == null) {
userInformation.append("\nfirstname : <u>null</u>"); userInformation.append("\nfirstname : <u>null</u>");
} else { } else {
@ -75,7 +78,7 @@ public class GetUsernameAndId {
firstname : firstname :
- <code>%s</code>""", - <code>%s</code>""",
user.firstName() escapeHtmlTelegram(user.firstName())
)); ));
} }
if (user.lastName() == null) { if (user.lastName() == null) {
@ -86,7 +89,7 @@ public class GetUsernameAndId {
lastname : lastname :
- <code>%s</code>""", - <code>%s</code>""",
user.lastName() escapeHtmlTelegram(user.lastName())
)); ));
} }
if (user.languageCode() != null) { if (user.languageCode() != null) {
@ -95,7 +98,7 @@ public class GetUsernameAndId {
language-code : language-code :
- <code>%s</code>""", - <code>%s</code>""",
user.languageCode() escapeHtmlTelegram(user.languageCode())
)); ));
} }

View File

@ -0,0 +1,52 @@
package cc.sukazyo.cono.morny.bot.event.on_commands;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.InputCommand;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.util.StringUtils.escapeHtmlTelegram;
/**
* {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端
* @since 0.4.2.10
*/
public class Ip186Query {
public static void exec (@Nonnull Update event, @Nonnull InputCommand command) {
if (!command.hasArgs()) { MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
"[Unavailable] No ip defined."
).replyToMessageId(event.message().messageId())); return; }
if (command.getArgs().length > 1) { MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
"[Unavailable] Too much arguments."
).replyToMessageId(event.message().messageId())); return; }
try {
IP186QueryResponse response = switch (command.getCommand()) {
case "/ip" -> IP186QueryHandler.queryIp(command.getArgs()[0]);
case "/whois" -> IP186QueryHandler.queryWhois(command.getArgs()[0]);
default -> throw new IllegalArgumentException("Unknown 186-IP query method " + command.getCommand());
};
MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
escapeHtmlTelegram(response.url()) + "\n<code>" + escapeHtmlTelegram(response.body()) + "</code>"
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
} catch (Exception e) {
MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(),
"[Exception] in query:\n<code>" + escapeHtmlTelegram(e.getMessage()) + "</code>"
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -0,0 +1,4 @@
package cc.sukazyo.cono.morny.bot.event.on_commands;
public class Roll {
}

View File

@ -0,0 +1,45 @@
package cc.sukazyo.cono.morny.data;
import cc.sukazyo.cono.morny.util.EncryptUtils;
import com.pengrad.telegrambot.model.User;
/**
* Morny jrrp 运算类.
*
* @see #getJrrpFromTelegramUser(User,long)
* @see #calcJrrpXmomi(long,long)
* @since 0.4.2.9
*/
public class MornyJrrp {
/**
* 通过 telegram 用户和时间戳作为参数获取 jrrp.
*
* @see #calcJrrpXmomi 当前版本的实现算法 {@code Xmomi}
* @since 0.4.2.9
* @param user telegram 用户
* @param timestamp 时间戳
* @return 通过当前版本的算法计算出的用户 jrrp 取值为 {@code [0.00, 100.00]}
*/
public static double getJrrpFromTelegramUser (User user, long timestamp) {
return calcJrrpXmomi(user.id(), timestamp / (1000 * 60 * 60 * 24)) * 100.0;
}
/**
* {@code Xmomi} 版本的 jrrp 算法.
* <p>
* 算法规则为将用户id与日期戳链接为 <u><code>uid@daystamp</code></u> 这样的字符串
* 然后通过 MD5 计算出字符串的哈希值取哈希值前4个字节将其作为16进制数值表示法转换为取值为 {@code [0x0000, 0xffff]} 的数值
* 得到的数值除以区间最大值 {@code 0xffff} 即可得到一个分布在 {@code [0.0, 1.0]} 之间的分布值
* 这个分布值乘以 {@code 100.0}即为计算得到的 jrrp 数值
*
* @since 0.4.2.9
* @param userId telegram 用户 uid
* @param dayStamp unix 时间戳转换为日期单位后的数值. 数值应该在转换前转换时区
* @return 算法得到的 jrrp 取值为 {@code [0.00. 100.00]}
*/
public static double calcJrrpXmomi (long userId, long dayStamp) {
return (double)Long.parseLong(EncryptUtils.encryptByMD5(userId + "@" + dayStamp).substring(0, 4), 16) / (double)0xffff;
}
}

View File

@ -0,0 +1,17 @@
package cc.sukazyo.cono.morny.data;
/**
* 存放 bot 使用到的贴纸
* @since 0.4.2.0
*/
public class TelegramStickers {
public static final String ID_ONLINE_STATUS_RETURN = "CAACAgEAAx0CW-CvvgAC5eBhhhODGRuu0pxKLwoQ3yMsowjviAACcycAAnj8xgVVU666si1utiIE";
public static final String ID_HELLO = "CAACAgEAAxkBAAMnYYYWKNXO4ibo9dlsmDctHhhV6fIAAqooAAJ4_MYFJJhrHS74xUAiBA";
public static final String ID_EXIT = "CAACAgEAAxkBAAMoYYYWt8UjvP0N405SAyvg2SQZmokAAkMiAAJ4_MYFw6yZLu06b-MiBA";
public static final String ID_403 = "CAACAgEAAxkBAAMqYYYa_7hpXH6hMOYMX4Nh8AVYd74AAnQnAAJ4_MYFRdmmsQKLDZgiBA";
public static final String ID_404 = "CAACAgEAAx0CSQh32gABA966YbRJpbmi2lCHINBDuo1DknSTsbsAAqUoAAJ4_MYFUa8SIaZriAojBA";
public static final String ID_WAITING = "CAACAgEAAx0CSQh32gABA-8DYbh7W2VhJ490ucfZMUMrgMR2FW4AAm4nAAJ4_MYFjx6zpxJPWsQjBA";
public static final String ID_SENT = "CAACAgEAAx0CSQh32gABA--zYbiyU_wOijEitp-0tSl_k7W6l3gAAgMmAAJ4_MYF4GrompjXPx4jBA";
}

View File

@ -0,0 +1,75 @@
package cc.sukazyo.cono.morny.data.ip186;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import javax.annotation.Nonnull;
import java.io.IOException;
/**
* 通过 {@value #SITE_URL} 进行 {@link #queryIp ip}/{@link #queryWhois whois} 数据查询的工具类
*
* @since 0.4.2.10
*/
public class IP186QueryHandler {
/**
* 请求所使用的 HTTP API 站点链接
* @since 0.4.2.10
*/
public static final String SITE_URL = "https://ip.186526.xyz/";
/**
* 进行 {@link #queryIp ip 查询}时所使用的 API 参数.<br>
* 目的使 API 直接返回原始数据
*/
private static final String QUERY_IP_PARAM = "type=json&format=true";
/**
* 进行 {@link #queryWhois whois 查询}时所使用的 API 参数.<br>
* 目的使 API 直接返回原始数据
*/
private static final String QUERY_WHOIS_PARAM = "type=plain";
/** 请求时使用的 OkHttp 请求工具实例 */
private static final OkHttpClient httpClient = new OkHttpClient();
/**
* 通过 {@value #SITE_URL} 获取 ip 信息.
* @see #QUERY_IP_PARAM 发送请求时所使用的 API 参数
* @param ip 需要进行查询的 ip
* @return 查询结果data 根据 {@value #SITE_URL} 的规则以 json 序列化
* @throws IOException 任何请求或解析错误
*/
@Nonnull
public static IP186QueryResponse queryIp (String ip) throws IOException {
final String requestUrl = SITE_URL + ip;
return commonQuery(requestUrl, QUERY_IP_PARAM);
}
/**
* 通过 {@value #SITE_URL} 获取域名信息.
* @see #QUERY_WHOIS_PARAM 发送请求时所使用的 API 参数
* @param domain 需要进行查询的域名
* @return 查询结果data 根据 {@value #SITE_URL} 的规则以 plain 序列化
* @throws IOException 任何请求或解析错误
*/
@Nonnull
public static IP186QueryResponse queryWhois (String domain) throws IOException {
final String requestUrl = SITE_URL + "whois/" + domain;
return commonQuery(requestUrl, QUERY_WHOIS_PARAM);
}
@Nonnull
private static IP186QueryResponse commonQuery (String requestUrl, String queryIpParam) throws IOException {
Request request = new Request.Builder().url(requestUrl + "?" + queryIpParam).build();
try (Response response = httpClient.newCall(request).execute()) {
final ResponseBody body = response.body();
if (body == null) throw new IOException("Null body.");
return new IP186QueryResponse(requestUrl, body.string());
}
}
}

View File

@ -0,0 +1,11 @@
package cc.sukazyo.cono.morny.data.ip186;
/**
* {@link IP186QueryHandler} 的请求结果数据的通用封装类.
*
* @since 0.4.2.10
* @param url 请求数据的<u>人类可读的</u>来源链接<b>并非api链接</b>
* @param body API 传回的数据内容
*/
public record IP186QueryResponse(String url, String body) {
}

View File

@ -1,5 +1,6 @@
package cc.sukazyo.cono.morny.util; package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnull;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -18,6 +19,7 @@ public class EncryptUtils {
/*** /***
* 对指定的字符串进行MD5加密 * 对指定的字符串进行MD5加密
*/ */
@Nonnull
public static String encryptByMD5(String originString) { public static String encryptByMD5(String originString) {
try { try {
//创建具有MD5算法的信息摘要 //创建具有MD5算法的信息摘要
@ -28,9 +30,8 @@ public class EncryptUtils {
String s = byteArrayToHex(bytes); String s = byteArrayToHex(bytes);
return s.toUpperCase(); return s.toUpperCase();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
e.printStackTrace(); throw new IllegalStateException();
} }
return null;
} }
/** /**

View File

@ -3,17 +3,9 @@ package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnegative; import javax.annotation.Nonnegative;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
public class StringUtils { public class StringUtils {
@Nonnull
public static String repeatChar (char c, int i) {
final char[] chars = new char[i];
Arrays.fill(chars, c);
return new String(chars);
}
@Nonnull @Nonnull
public static String[] formatCommand (@Nonnull String com) { public static String[] formatCommand (@Nonnull String com) {
@ -66,4 +58,12 @@ public class StringUtils {
return builder.toString(); return builder.toString();
} }
@Nonnull
public static String escapeHtmlTelegram (String raw) {
raw = raw.replaceAll("&", "&amp;");
raw = raw.replaceAll("<", "&lt;");
raw = raw.replaceAll(">", "&gt;");
return raw;
}
} }