diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f9ff432 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ + +# IDE +.idea/ +.vscode/ +.gradle/ +.settings/ + +#build +/build/ +/bin/ +.metals/ +.bloop/ +.project +lcoal.properties + +# debug dir +/run/ diff --git a/.editorconfig b/.editorconfig index ee8e124..424d5bf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,3 @@ -root = true - [*] charset = utf-8 end_of_line = lf @@ -11,11 +9,50 @@ 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_formatter_tags_enabled = true ij_smart_tabs = false -ij_visual_guides = none +ij_visual_guides = ij_wrap_on_typing = false +[*.conf] +ij_smart_tabs = true +ij_hocon_keep_blank_lines_before_right_brace = 2 +ij_hocon_keep_indents_on_empty_lines = true +ij_hocon_keep_line_breaks = true +ij_hocon_space_after_colon = true +ij_hocon_space_after_comma = true +ij_hocon_space_before_colon = true +ij_hocon_space_before_comma = false +ij_hocon_spaces_within_braces = false +ij_hocon_spaces_within_brackets = false +ij_hocon_spaces_within_method_call_parentheses = false + +[*.css] +ij_smart_tabs = true +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_block_comment_add_space = false +ij_css_brace_placement = end_of_line +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = true +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = true +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = do_not_align + +[*.feature] +indent_size = 2 +indent_style = space +ij_gherkin_keep_indents_on_empty_lines = false + [*.java] ij_java_align_consecutive_assignments = false ij_java_align_consecutive_variable_declarations = false @@ -25,6 +62,7 @@ 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_deconstruction_list_components = true ij_java_align_multiline_extends_list = false ij_java_align_multiline_for = false ij_java_align_multiline_method_parentheses = false @@ -38,6 +76,7 @@ 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_align_types_in_multi_catch = 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 @@ -64,7 +103,7 @@ 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_builder_methods = 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 @@ -74,8 +113,10 @@ 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_deconstruction_list_wrap = normal ij_java_do_not_indent_top_level_class_members = false ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = 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 @@ -96,18 +137,31 @@ 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_prefix = ij_java_entity_dd_suffix = EJB +ij_java_entity_eb_prefix = ij_java_entity_eb_suffix = Bean +ij_java_entity_hi_prefix = 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_li_suffix = ij_java_entity_pk_class = java.lang.String +ij_java_entity_ri_prefix = +ij_java_entity_ri_suffix = +ij_java_entity_vo_prefix = 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_field_name_prefix = +ij_java_field_name_suffix = +ij_java_filter_class_prefix = +ij_java_filter_class_suffix = +ij_java_filter_dd_prefix = +ij_java_filter_dd_suffix = ij_java_finally_on_new_line = false ij_java_for_brace_force = if_multiline ij_java_for_statement_new_line_after_left_paren = true @@ -139,8 +193,15 @@ 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_add_space_on_reformat = false ij_java_line_comment_at_first_column = true +ij_java_listener_class_prefix = +ij_java_listener_class_suffix = +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_message_dd_prefix = ij_java_message_dd_suffix = EJB +ij_java_message_eb_prefix = ij_java_message_eb_suffix = Bean ij_java_method_annotation_wrap = split_into_lines ij_java_method_brace_style = end_of_line @@ -149,16 +210,22 @@ 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_multi_catch_types_wrap = normal ij_java_names_count_to_use_import_on_demand = 3 +ij_java_new_line_after_lparen_in_annotation = false +ij_java_new_line_after_lparen_in_deconstruction_pattern = true 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_parameter_name_prefix = +ij_java_parameter_name_suffix = 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_annotations = ij_java_repeat_synchronized = true ij_java_replace_instanceof_and_cast = false ij_java_replace_null_check = true @@ -166,13 +233,26 @@ 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_annotation = false +ij_java_rparen_on_new_line_in_deconstruction_pattern = true ij_java_rparen_on_new_line_in_record_header = false +ij_java_servlet_class_prefix = +ij_java_servlet_class_suffix = +ij_java_servlet_dd_prefix = +ij_java_servlet_dd_suffix = +ij_java_session_dd_prefix = ij_java_session_dd_suffix = EJB +ij_java_session_eb_prefix = ij_java_session_eb_suffix = Bean +ij_java_session_hi_prefix = 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_li_suffix = +ij_java_session_ri_prefix = +ij_java_session_ri_suffix = +ij_java_session_si_prefix = ij_java_session_si_suffix = Service ij_java_space_after_closing_angle_bracket_in_type_argument = false ij_java_space_after_colon = true @@ -191,6 +271,7 @@ 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_deconstruction_list = false ij_java_space_before_do_left_brace = true ij_java_space_before_else_keyword = true ij_java_space_before_else_left_brace = true @@ -221,6 +302,7 @@ 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_annotation_eq = true ij_java_spaces_around_assignment_operators = true ij_java_spaces_around_bitwise_operators = true ij_java_spaces_around_equality_operators = true @@ -239,6 +321,7 @@ 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_deconstruction_list = false ij_java_spaces_within_for_parentheses = false ij_java_spaces_within_if_parentheses = false ij_java_spaces_within_method_call_parentheses = false @@ -250,9 +333,13 @@ 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_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = 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_prefix = ij_java_test_name_suffix = Test ij_java_throws_keyword_wrap = normal ij_java_throws_list_wrap = off @@ -268,6 +355,303 @@ ij_java_wrap_comments = false ij_java_wrap_first_method_in_call_chain = true ij_java_wrap_long_lines = false +[*.less] +indent_size = 2 +indent_style = space +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_block_comment_add_space = false +ij_less_brace_placement = 0 +ij_less_enforce_quotes_on_format = false +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_line_comment_add_space = false +ij_less_line_comment_at_first_column = false +ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_use_double_quotes = true +ij_less_value_alignment = 0 + +[*.proto] +indent_size = 2 +indent_style = space +tab_width = 2 +ij_continuation_indent_size = 4 +ij_protobuf_keep_blank_lines_in_code = 2 +ij_protobuf_keep_indents_on_empty_lines = false +ij_protobuf_keep_line_breaks = true +ij_protobuf_space_after_comma = true +ij_protobuf_space_before_comma = false +ij_protobuf_spaces_around_assignment_operators = true +ij_protobuf_spaces_within_braces = false +ij_protobuf_spaces_within_brackets = false + +[*.sass] +indent_size = 2 +indent_style = space +ij_sass_align_closing_brace_with_properties = false +ij_sass_blank_lines_around_nested_selector = 1 +ij_sass_blank_lines_between_blocks = 1 +ij_sass_brace_placement = 0 +ij_sass_enforce_quotes_on_format = false +ij_sass_hex_color_long_format = false +ij_sass_hex_color_lower_case = false +ij_sass_hex_color_short_format = false +ij_sass_hex_color_upper_case = false +ij_sass_keep_blank_lines_in_code = 2 +ij_sass_keep_indents_on_empty_lines = false +ij_sass_keep_single_line_blocks = false +ij_sass_line_comment_add_space = false +ij_sass_line_comment_at_first_column = false +ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_space_after_colon = true +ij_sass_space_before_opening_brace = true +ij_sass_use_double_quotes = true +ij_sass_value_alignment = 0 + +[*.scala] +ij_continuation_indent_size = 4 +ij_smart_tabs = true +ij_scala_align_composite_pattern = true +ij_scala_align_extends_with = 0 +ij_scala_align_group_field_declarations = false +ij_scala_align_if_else = false +ij_scala_align_in_columns_case_branch = false +ij_scala_align_multiline_binary_operation = false +ij_scala_align_multiline_chained_methods = false +ij_scala_align_multiline_for = true +ij_scala_align_multiline_parameters = false +ij_scala_align_multiline_parameters_in_calls = false +ij_scala_align_multiline_parenthesized_expression = false +ij_scala_align_parameter_types_in_multiline_declarations = 0 +ij_scala_align_tuple_elements = false +ij_scala_alternate_continuation_indent_for_params = 4 +ij_scala_binary_operation_wrap = off +ij_scala_blank_lines_after_anonymous_class_header = 0 +ij_scala_blank_lines_after_class_header = 0 +ij_scala_blank_lines_after_imports = 1 +ij_scala_blank_lines_after_package = 1 +ij_scala_blank_lines_around_class = 1 +ij_scala_blank_lines_around_class_in_inner_scopes = 0 +ij_scala_blank_lines_around_field = 0 +ij_scala_blank_lines_around_field_in_inner_scopes = 0 +ij_scala_blank_lines_around_field_in_interface = 0 +ij_scala_blank_lines_around_method = 1 +ij_scala_blank_lines_around_method_in_inner_scopes = 1 +ij_scala_blank_lines_around_method_in_interface = 1 +ij_scala_blank_lines_before_class_end = 0 +ij_scala_blank_lines_before_imports = 1 +ij_scala_blank_lines_before_method_body = 0 +ij_scala_blank_lines_before_package = 0 +ij_scala_block_brace_style = end_of_line +ij_scala_block_comment_add_space = false +ij_scala_block_comment_at_first_column = true +ij_scala_call_parameters_new_line_after_lparen = 0 +ij_scala_call_parameters_right_paren_on_new_line = false +ij_scala_call_parameters_wrap = off +ij_scala_case_clause_brace_force = never +ij_scala_catch_on_new_line = false +ij_scala_class_annotation_wrap = split_into_lines +ij_scala_class_brace_style = end_of_line +ij_scala_closure_brace_force = never +ij_scala_do_not_align_block_expr_params = true +ij_scala_do_not_indent_case_clause_body = false +ij_scala_do_not_indent_tuples_close_brace = true +ij_scala_do_while_brace_force = never +ij_scala_else_on_new_line = false +ij_scala_enable_scaladoc_formatting = true +ij_scala_enforce_functional_syntax_for_unit = true +ij_scala_extends_keyword_wrap = off +ij_scala_extends_list_wrap = off +ij_scala_field_annotation_wrap = split_into_lines +ij_scala_finally_brace_force = never +ij_scala_finally_on_new_line = false +ij_scala_for_brace_force = never +ij_scala_for_statement_wrap = off +ij_scala_formatter = 0 +ij_scala_if_brace_force = never +ij_scala_implicit_value_class_prefix = +ij_scala_implicit_value_class_suffix = Ops +ij_scala_indent_braced_function_args = true +ij_scala_indent_case_from_switch = true +ij_scala_indent_fewer_braces_in_method_call_chains = false +ij_scala_indent_first_parameter = true +ij_scala_indent_first_parameter_clause = false +ij_scala_indent_type_arguments = true +ij_scala_indent_type_parameters = true +ij_scala_indent_yield_after_one_line_enumerators = true +ij_scala_keep_blank_lines_before_right_brace = 2 +ij_scala_keep_blank_lines_in_code = 2 +ij_scala_keep_blank_lines_in_declarations = 2 +ij_scala_keep_comments_on_same_line = true +ij_scala_keep_first_column_comment = false +ij_scala_keep_indents_on_empty_lines = true +ij_scala_keep_line_breaks = true +ij_scala_keep_one_line_lambdas_in_arg_list = false +ij_scala_keep_simple_blocks_in_one_line = false +ij_scala_keep_simple_methods_in_one_line = false +ij_scala_keep_xml_formatting = false +ij_scala_line_comment_add_space = false +ij_scala_line_comment_at_first_column = true +ij_scala_method_annotation_wrap = split_into_lines +ij_scala_method_brace_force = never +ij_scala_method_brace_style = end_of_line +ij_scala_method_call_chain_wrap = off +ij_scala_method_parameters_new_line_after_left_paren = true +ij_scala_method_parameters_right_paren_on_new_line = true +ij_scala_method_parameters_wrap = off +ij_scala_modifier_list_wrap = false +ij_scala_multiline_string_align_dangling_closing_quotes = false +ij_scala_multiline_string_closing_quotes_on_new_line = true +ij_scala_multiline_string_insert_margin_on_enter = true +ij_scala_multiline_string_margin_char = | +ij_scala_multiline_string_margin_indent = 2 +ij_scala_multiline_string_opening_quotes_on_new_line = true +ij_scala_multiline_string_process_margin_on_copy_paste = true +ij_scala_new_line_after_case_clause_arrow_when_multiline_body = false +ij_scala_newline_after_annotations = false +ij_scala_not_continuation_indent_for_params = false +ij_scala_parameter_annotation_wrap = off +ij_scala_parentheses_expression_new_line_after_left_paren = false +ij_scala_parentheses_expression_right_paren_on_new_line = false +ij_scala_place_closure_parameters_on_new_line = false +ij_scala_place_self_type_on_new_line = true +ij_scala_prefer_parameters_wrap = false +ij_scala_preserve_space_after_method_declaration_name = false +ij_scala_reformat_on_compile = false +ij_scala_replace_case_arrow_with_unicode_char = false +ij_scala_replace_for_generator_arrow_with_unicode_char = false +ij_scala_replace_lambda_with_greek_letter = false +ij_scala_replace_map_arrow_with_unicode_char = false +ij_scala_scalafmt_config_path = +ij_scala_scalafmt_fallback_to_default_settings = false +ij_scala_scalafmt_reformat_on_files_save = false +ij_scala_scalafmt_show_invalid_code_warnings = true +ij_scala_scalafmt_use_intellij_formatter_for_range_format = true +ij_scala_sd_align_exception_comments = true +ij_scala_sd_align_list_item_content = true +ij_scala_sd_align_other_tags_comments = true +ij_scala_sd_align_parameters_comments = true +ij_scala_sd_align_return_comments = true +ij_scala_sd_blank_line_after_parameters_comments = false +ij_scala_sd_blank_line_after_return_comments = false +ij_scala_sd_blank_line_before_parameters = false +ij_scala_sd_blank_line_before_tags = true +ij_scala_sd_blank_line_between_parameters = false +ij_scala_sd_keep_blank_lines_between_tags = true +ij_scala_sd_preserve_spaces_in_tags = false +ij_scala_space_after_comma = true +ij_scala_space_after_for_semicolon = true +ij_scala_space_after_modifiers_constructor = true +ij_scala_space_after_type_colon = true +ij_scala_space_before_brace_method_call = true +ij_scala_space_before_class_left_brace = true +ij_scala_space_before_for_parentheses = true +ij_scala_space_before_if_parentheses = true +ij_scala_space_before_infix_like_method_parentheses = false +ij_scala_space_before_infix_method_call_parentheses = false +ij_scala_space_before_infix_operator_like_method_call_parentheses = false +ij_scala_space_before_method_call_parentheses = false +ij_scala_space_before_method_left_brace = true +ij_scala_space_before_method_parentheses = true +ij_scala_space_before_type_colon = false +ij_scala_space_before_type_parameter_in_def_list = false +ij_scala_space_before_type_parameter_leading_context_bound_colon = false +ij_scala_space_before_type_parameter_leading_context_bound_colon_hk = true +ij_scala_space_before_type_parameter_list = false +ij_scala_space_before_type_parameter_rest_context_bound_colons = true +ij_scala_space_before_while_parentheses = true +ij_scala_space_inside_closure_braces = true +ij_scala_space_inside_self_type_braces = true +ij_scala_space_within_empty_method_call_parentheses = false +ij_scala_spaces_around_at_in_patterns = false +ij_scala_spaces_in_imports = false +ij_scala_spaces_in_one_line_blocks = false +ij_scala_spaces_within_brackets = false +ij_scala_spaces_within_for_parentheses = false +ij_scala_spaces_within_if_parentheses = false +ij_scala_spaces_within_method_call_parentheses = false +ij_scala_spaces_within_method_parentheses = false +ij_scala_spaces_within_parentheses = false +ij_scala_spaces_within_while_parentheses = false +ij_scala_special_else_if_treatment = true +ij_scala_trailing_comma_arg_list_enabled = true +ij_scala_trailing_comma_import_selector_enabled = false +ij_scala_trailing_comma_mode = trailing_comma_keep +ij_scala_trailing_comma_params_enabled = true +ij_scala_trailing_comma_pattern_arg_list_enabled = false +ij_scala_trailing_comma_tuple_enabled = false +ij_scala_trailing_comma_tuple_type_enabled = false +ij_scala_trailing_comma_type_params_enabled = false +ij_scala_try_brace_force = never +ij_scala_type_annotation_exclude_constant = true +ij_scala_type_annotation_exclude_in_dialect_sources = true +ij_scala_type_annotation_exclude_in_test_sources = false +ij_scala_type_annotation_exclude_member_of_anonymous_class = false +ij_scala_type_annotation_exclude_member_of_private_class = false +ij_scala_type_annotation_exclude_when_type_is_stable = true +ij_scala_type_annotation_function_parameter = false +ij_scala_type_annotation_implicit_modifier = true +ij_scala_type_annotation_local_definition = false +ij_scala_type_annotation_private_member = false +ij_scala_type_annotation_protected_member = true +ij_scala_type_annotation_public_member = true +ij_scala_type_annotation_structural_type = true +ij_scala_type_annotation_underscore_parameter = false +ij_scala_type_annotation_unit_type = true +ij_scala_use_alternate_continuation_indent_for_params = false +ij_scala_use_scala3_indentation_based_syntax = true +ij_scala_use_scaladoc2_formatting = true +ij_scala_variable_annotation_wrap = off +ij_scala_while_brace_force = never +ij_scala_while_on_new_line = false +ij_scala_wrap_before_with_keyword = false +ij_scala_wrap_first_method_in_call_chain = false +ij_scala_wrap_long_lines = false + +[*.scss] +indent_size = 2 +indent_style = space +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_block_comment_add_space = false +ij_scss_brace_placement = 0 +ij_scss_enforce_quotes_on_format = false +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 2 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_line_comment_add_space = false +ij_scss_line_comment_at_first_column = false +ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_use_double_quotes = true +ij_scss_value_alignment = 0 + +[*.vue] +indent_size = 2 +indent_style = space +tab_width = 2 +ij_continuation_indent_size = 4 +ij_vue_indent_children_of_top_level = template +ij_vue_interpolation_new_line_after_start_delimiter = true +ij_vue_interpolation_new_line_before_end_delimiter = true +ij_vue_interpolation_wrap = off +ij_vue_keep_indents_on_empty_lines = false +ij_vue_spaces_within_interpolation_expressions = true + [.editorconfig] ij_editorconfig_align_group_field_declarations = false ij_editorconfig_space_after_colon = false @@ -276,7 +660,7 @@ 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}] +[{*.ant,*.fxml,*.isc,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] ij_smart_tabs = true ij_xml_align_attributes = false ij_xml_align_text = false @@ -297,7 +681,367 @@ ij_xml_space_inside_empty_tag = true ij_xml_text_wrap = normal ij_xml_use_custom_settings = false -[{*.gant,*.groovy,*.gson,*.gy}] +[{*.ats,*.cts,*.mts,*.ts}] +ij_continuation_indent_size = 4 +ij_smart_tabs = true +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = false +ij_typescript_align_multiline_parameters = false +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_block_comment_add_space = false +ij_typescript_block_comment_at_first_column = true +ij_typescript_call_parameters_new_line_after_left_paren = true +ij_typescript_call_parameters_right_paren_on_new_line = true +ij_typescript_call_parameters_wrap = normal +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_enum_constants_wrap = on_every_item +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = true +ij_typescript_for_statement_right_paren_on_new_line = true +ij_typescript_for_statement_wrap = on_every_item +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = false +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = false +ij_typescript_keep_indents_on_empty_lines = true +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = true +ij_typescript_method_parameters_right_paren_on_new_line = true +ij_typescript_method_parameters_wrap = on_every_item +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_object_types_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = true +ij_typescript_parentheses_expression_right_paren_on_new_line = true +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_explicit_types_function_expression_returns = false +ij_typescript_prefer_explicit_types_function_returns = false +ij_typescript_prefer_explicit_types_vars_fields = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_property_prefix = +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = true +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = false +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = auto +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + +[{*.bash,*.sh,*.zsh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = true +ij_shell_use_unix_line_separator = true + +[{*.cjs,*.js}] +ij_continuation_indent_size = 4 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = true +ij_javascript_array_initializer_right_brace_on_new_line = true +ij_javascript_array_initializer_wrap = on_every_item +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_block_comment_add_space = false +ij_javascript_block_comment_at_first_column = true +ij_javascript_call_parameters_new_line_after_left_paren = true +ij_javascript_call_parameters_right_paren_on_new_line = true +ij_javascript_call_parameters_wrap = on_every_item +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = false +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = remove +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = if_multiline +ij_javascript_for_statement_new_line_after_left_paren = true +ij_javascript_for_statement_right_paren_on_new_line = true +ij_javascript_for_statement_wrap = on_every_item +ij_javascript_force_quote_style = false +ij_javascript_force_semicolon_style = false +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = false +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 2 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = true +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = true +ij_javascript_method_parameters_right_paren_on_new_line = true +ij_javascript_method_parameters_wrap = on_every_item +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_object_types_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_explicit_types_function_expression_returns = false +ij_javascript_prefer_explicit_types_function_returns = false +ij_javascript_prefer_explicit_types_vars_fields = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_property_prefix = +ij_javascript_reformat_c_style_comments = true +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = true +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = true +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = false +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = false +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = on_every_item +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = false +ij_javascript_use_explicit_js_extension = auto +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = false +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.comp,*.frag,*.fsh,*.geom,*.glsl,*.tesc,*.tese,*.vert,*.vsh}] +ij_glsl_keep_indents_on_empty_lines = true + +[{*.ft,*.vm,*.vsl}] +ij_smart_tabs = true +ij_vtl_keep_indents_on_empty_lines = true + +[{*.gant,*.groovy,*.gy}] ij_smart_tabs = true ij_groovy_align_group_field_declarations = false ij_groovy_align_multiline_array_initializer_expression = false @@ -344,6 +1088,7 @@ 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_enable_groovydoc_formatting = true ij_groovy_enum_constants_wrap = on_every_item ij_groovy_extends_keyword_wrap = normal ij_groovy_extends_list_wrap = off @@ -353,6 +1098,12 @@ 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_ginq_general_clause_wrap_policy = 2 +ij_groovy_ginq_having_wrap_policy = 1 +ij_groovy_ginq_indent_having_clause = true +ij_groovy_ginq_indent_on_clause = true +ij_groovy_ginq_on_wrap_policy = 1 +ij_groovy_ginq_space_after_keyword = true ij_groovy_if_brace_force = never ij_groovy_import_annotation_wrap = 2 ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* @@ -376,6 +1127,7 @@ 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_add_space_on_reformat = 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 @@ -481,6 +1233,109 @@ ij_groovy_while_on_new_line = false ij_groovy_wrap_chain_calls_after_dot = false ij_groovy_wrap_long_lines = false +[{*.gradle.kts,*.kt,*.kts,*.main.kts,*.space.kts}] +ij_smart_tabs = true +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = true +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_assignment_wrap = normal +ij_kotlin_blank_lines_after_class_header = 1 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 +ij_kotlin_block_comment_add_space = false +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = true +ij_kotlin_call_parameters_right_paren_on_new_line = true +ij_kotlin_call_parameters_wrap = on_every_item +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_continuation_indent_for_chained_calls = false +ij_kotlin_continuation_indent_for_expression_bodies = false +ij_kotlin_continuation_indent_in_argument_lists = false +ij_kotlin_continuation_indent_in_elvis = false +ij_kotlin_continuation_indent_in_if_conditions = false +ij_kotlin_continuation_indent_in_parameter_lists = false +ij_kotlin_continuation_indent_in_supertype_lists = false +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = on_every_item +ij_kotlin_extends_list_wrap = on_every_item +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = true +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = false +ij_kotlin_keep_indents_on_empty_lines = true +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = on_every_item +ij_kotlin_method_parameters_new_line_after_left_paren = true +ij_kotlin_method_parameters_right_paren_on_new_line = true +ij_kotlin_method_parameters_wrap = on_every_item +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 1 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.conf,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config,mcmod.info,meatball_from_mutton.json,pack.mcmeta}] +ij_smart_tabs = true +ij_json_array_wrapping = split_into_lines +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = true +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = split_into_lines +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = false +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_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 @@ -491,7 +1346,7 @@ 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_do_not_indent_children_of_tags = 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 @@ -510,19 +1365,56 @@ ij_html_space_around_equality_in_attribute = false ij_html_space_inside_empty_tag = true ij_html_text_wrap = off +[{*.http,*.rest}] +indent_size = 0 +ij_continuation_indent_size = 4 +ij_http-request_call_parameters_wrap = normal +ij_http-request_method_parameters_wrap = split_into_lines +ij_http-request_space_before_comma = true +ij_http-request_spaces_around_assignment_operators = true + +[{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}] +ij_smart_tabs = true +ij_jsp_jsp_prefer_comma_separated_import_list = false +ij_jsp_keep_indents_on_empty_lines = true + +[{*.jspx,*.tagx}] +ij_smart_tabs = true +ij_jspx_keep_indents_on_empty_lines = true + [{*.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_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true ij_markdown_keep_indents_on_empty_lines = true +ij_markdown_keep_line_breaks_inside_text_blocks = 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 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.pb,*.textproto}] +indent_size = 2 +indent_style = space +tab_width = 2 +ij_continuation_indent_size = 4 +ij_prototext_keep_blank_lines_in_code = 2 +ij_prototext_keep_indents_on_empty_lines = false +ij_prototext_keep_line_breaks = true +ij_prototext_space_after_colon = true +ij_prototext_space_after_comma = true +ij_prototext_space_before_colon = false +ij_prototext_space_before_comma = false +ij_prototext_spaces_within_braces = true +ij_prototext_spaces_within_brackets = false [{*.properties,spring.handlers,spring.schemas}] ij_properties_align_group_field_declarations = false @@ -530,3 +1422,23 @@ ij_properties_keep_blank_lines = true ij_properties_key_value_delimiter = equals ij_properties_spaces_around_key_value_delimiter = true +[{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}] +indent_style = space +ij_qute_keep_indents_on_empty_lines = false + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] +indent_style = space +ij_toml_keep_indents_on_empty_lines = false + +[{*.yaml,*.yml}] +indent_size = 2 +ij_yaml_align_values_properties = do_not_align +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_block_mapping_on_new_line = false +ij_yaml_indent_sequence_value = true +ij_yaml_keep_indents_on_empty_lines = true +ij_yaml_keep_line_breaks = true +ij_yaml_sequence_on_new_line = false +ij_yaml_space_before_colon = false +ij_yaml_spaces_within_braces = true +ij_yaml_spaces_within_brackets = true diff --git a/.gitignore b/.gitignore index 881a611..f02e4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,15 @@ .vscode/ .gradle/ .settings/ -/src/test/java/test/* -/src/test/resources/test/* #build /build/ /bin/ +/out/ +.metals/ +.bloop/ .project +lcoal.properties # debug dir /run/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index aa5e2ed..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "_book"] - path = _book - url = https://storage.sukazyo.cc/Eyre_S/morny-book.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..055ebfc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM eclipse-temurin:20-jdk as build +LABEL authors="A.C.Sukazyo Eyre" + +COPY . /app/source/ +WORKDIR /app + +RUN cd ./source \ +&& ./gradlew shadowJar -PdockerBuild \ +&& cd .. \ +&& cp ./source/build/libs/morny-coeur-docker-build.jar ./morny-coeur.jar +#&& rm -r ./source \ +#&& rm -r /root/.gradle \ + + +FROM eclipse-temurin:20-jre + +COPY --from=build /app/morny-coeur.jar /app/morny-coeur.jar +WORKDIR /app + +ENTRYPOINT ["java", "-jar", "morny-coeur.jar"] +CMD ["-q", "-v"] diff --git a/README.md b/README.md index 99bf217..22e0fb8 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,22 @@ [todo]: https://github.com/users/Eyre-S/projects/1 [artifact]: https://mvn.sukazyo.cc/#/releases/cc/sukazyo/morny-coeur -[tg4j]: https://github.com/pengrad/java-telegram-bot-api +[scala]: https://www.scala-lang.org/ [spotbugs]: https://spotbugs.github.io/ -[junit5]: https://junit.org/junit5/ +[tg4j]: https://github.com/pengrad/java-telegram-bot-api +[okhttp]: https://square.github.io/okhttp/ +[gson]: https://github.com/google/gson +[scalatest]: https://scalatest.org/
- * 如果 morny 已经初始化,则不会进行初始化,抛出错误消息并直接退出方法。
- *
- * @see #MornyCoeur 程序初始化方法
- */
- public static void main (
- @Nullable String botApi, @Nullable String botApi4File,
- @Nonnull String botKey, @Nullable String botUsername,
- long master, long trustedChat, Set
- * 只支持以下事件
- *
- * 算法规则为,将用户id与日期戳链接为
- * the AV id is a number; the BV id is a special base58 number, it shows as String in programming.
- * for now , the BV id has 10 digits.
- * the method available while the av-id < 2^27, while it theoretically available when the av-id < 2^30.
- *
- * @see mcfx的回复: 如何看待 2020 年 3 月 23 日哔哩哔哩将稿件的「av 号」变更为「BV 号」?
- *
- * @param bv the BV id, a string in (a special) base58 number format, without "BV" prefix.
- * @return the AV id corresponding to this bv id in Bilibili, formatted as a number.
- */
- @Nonnegative
- public static long toAv (@Nonnull String bv) {
- long av = 0;
- for (int i = 0; i < BV_TEMPLATE_FILTER.length; i++) {
- av += BV_TABLE_REVERSED.get(bv.charAt(BV_TEMPLATE_FILTER[i])) * Math.pow(TABLE_INT,i);
- }
- return (av-V_CONV_ADD)^V_CONV_XOR;
- }
-
- /**
- * Convert a Bilibili BV video id format to AV id format.
- *
- * the AV id is a number; the BV id is a special base58 number, it shows as String in programming.
- * for now , the BV id has 10 digits.
- * the method available while the av-id < 2^27, while it theoretically available when the av-id < 2^30.
- *
- * @see mcfx的回复: 如何看待 2020 年 3 月 23 日哔哩哔哩将稿件的「av 号」变更为「BV 号」?
- *
- * @param av the (base10) AV id.
- * @return the AV id corresponding to this bv id in Bilibili,
- * as a (special) base 58 number format without "BV" prefix.
- */
- @Nonnull
- public static String toBv (@Nonnegative long av) {
- av = (av^V_CONV_XOR)+V_CONV_ADD;
- final char[] bv = BV_TEMPLATE.clone();
- for (int i = 0; i < BV_TEMPLATE_FILTER.length; i++) {
- bv[BV_TEMPLATE_FILTER[i]] = BV_TABLE[(int)(Math.floor(av/(Math.pow(TABLE_INT, i)))%TABLE_INT)];
- }
- return String.copyValueOf(bv);
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/CommonConvert.java b/src/main/java/cc/sukazyo/cono/morny/util/CommonConvert.java
deleted file mode 100644
index 907cdd5..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/CommonConvert.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import javax.annotation.Nonnegative;
-import javax.annotation.Nonnull;
-
-/**
- * 进行简单类型转换等工作的类.
- */
-public class CommonConvert {
-
- /**
- * 将字节数组转换成 hex 字符串.
- * @param b 字节数组
- * @return String 格式的字节数组的 hex 值(每个字节当中没有分隔符)
- * @see #byteToHex(byte)
- */
- @Nonnull
- public static String byteArrayToHex(@Nonnull byte[] b){
- StringBuilder sb = new StringBuilder();
- for (byte value : b) {
- sb.append(byteToHex(value));
- }
- return sb.toString();
- }
-
- /**
- * 将一个字节转换成十六进制 hex 字符串.
- * @param b 字节值
- * @return String 格式的字节的 hex 值(小写)
- */
- @Nonnull
- public static String byteToHex(byte b) {
- final String hex = Integer.toHexString(b & 0xff);
- return hex.length()<2?"0"+hex:hex;
- }
-
- /**
- * 将一个字符串数组按照一定规则连接.
- *
- * 连接的方式类似于"数据1+分隔符+数据2+分隔符+...+数据n-1+分隔符+数据n"
- *
- * @param array 需要进行连接的字符串数组,数组中每一个元素会是一个数据
- * @param connector 在每两个传入数据中插入的分隔符
- * @param startIndex 从传入的数据组中的哪一个位置开始(第一个元素的位置是 {@code 0})
- * @param stopIndex 从传入的数据组中的哪一个位置停止(元素位置计算方式同上)
- * @return 连接好的字符串
- */
- @Nonnull
- public static String stringsConnecting (
- @Nonnull String[] array, @Nonnull String connector, @Nonnegative int startIndex, @Nonnegative int stopIndex
- ) {
- final StringBuilder builder = new StringBuilder();
- for (int i = startIndex; i < stopIndex; i++) {
- builder.append(array[i]);
- builder.append(connector);
- }
- builder.append(array[stopIndex]);
- return builder.toString();
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java b/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java
deleted file mode 100644
index 0e9c35d..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import javax.annotation.Nonnull;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Base64;
-
-/**
- * 用于数据加密或编解码的工具类.
- *
- * 出于 java std 中 Base64 的 {@link Base64.Encoder encode}/{@link Base64.Decoder decode} 十分好用,在此不再进行包装。
- */
-public class CommonEncrypt {
-
- /**
- * 在使用加密算法处理字符串时默认会使用的字符串编码.
- *
- * Morny 使用 UTF-8 编码因为这是一般而言加解密工具的默认行为
- */
- public static final Charset ENCRYPT_STANDARD_CHARSET = StandardCharsets.UTF_8;
-
- @Nonnull
- private static byte[] hashAsJavaMessageDigest(String algorithm, @Nonnull byte[] data) {
- try {
- return MessageDigest.getInstance(algorithm).digest(data);
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * 取得数据的 md5 散列值.
- *
- * @param data byte 数组形式的数据体
- * @return 二进制(byte数组)格式的数据的 md5 散列值
- */
- @Nonnull
- public static byte[] hashMd5 (@Nonnull byte[] data) {
- return hashAsJavaMessageDigest("md5", data);
- }
-
- /**
- * 取得一个字符串的 md5 散列值.
- *
- * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析
- *
- * @param originString 要进行散列的字符串
- * @return 二进制(byte数组)格式的 md5 散列值
- */
- @Nonnull
- public static byte[] hashMd5 (String originString) {
- return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET));
- }
-
- /**
- * 取得数据的 sha1 散列值.
- *
- * @param data byte 数组形式的数据体
- * @return 二进制(byte数组)格式的数据的 sha1 散列值
- */
- @Nonnull
- public static byte[] hashSha1 (@Nonnull byte[] data) {
- return hashAsJavaMessageDigest("sha1", data);
- }
-
- /**
- * 取得一个字符串的 sha1 散列值.
- *
- * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析
- *
- * @param originString 要进行散列的字符串
- * @return 二进制(byte数组)格式的 sha1 散列值
- */
- @Nonnull
- public static byte[] hashSha1 (String originString) {
- return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET));
- }
-
- /**
- * 取得数据的 sha256 散列值.
- *
- * @param data byte 数组形式的数据体
- * @return 二进制(byte数组)格式的数据的 sha256 散列值
- */
- @Nonnull
- public static byte[] hashSha256 (@Nonnull byte[] data) {
- return hashAsJavaMessageDigest("sha256", data);
- }
-
- /**
- * 取得一个字符串的 sha256 散列值.
- *
- * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析
- *
- * @param originString 要进行散列的字符串
- * @return 二进制(byte数组)格式的 sha256 散列值
- */
- @Nonnull
- public static byte[] hashSha256 (String originString) {
- return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET));
- }
-
- /**
- * 取得数据的 sha512 散列值.
- *
- * @param data byte 数组形式的数据体
- * @return 二进制(byte数组)格式的数据的 sha512 散列值
- */
- @Nonnull
- public static byte[] hashSha512 (@Nonnull byte[] data) {
- return hashAsJavaMessageDigest("md5", data);
- }
-
- /**
- * 取得一个字符串的 sha512 散列值.
- *
- * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析
- *
- * @param originString 要进行散列的字符串
- * @return 二进制(byte数组)格式的 sha512 散列值
- */
- @Nonnull
- public static byte[] hashSha512 (String originString) {
- return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET));
- }
-
- @Nonnull
- public static String base64FilenameLint (String inputName) {
- if (inputName.endsWith(".b64")) {
- return inputName.substring(0, inputName.length()-".b64".length());
- } else if (inputName.endsWith(".b64.txt")) {
- return inputName.substring(0, inputName.length()-".b64.txt".length());
- } else if (inputName.endsWith(".base64")) {
- return inputName.substring(0, inputName.length()-".base64".length());
- } else if (inputName.endsWith(".base64.txt")) {
- return inputName.substring(0, inputName.length()-".base64.txt".length());
- } else {
- return inputName;
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/CommonFormat.java b/src/main/java/cc/sukazyo/cono/morny/util/CommonFormat.java
deleted file mode 100644
index 0598ba6..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/CommonFormat.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-
-public class CommonFormat {
-
- public static final String DATE_TIME_PATTERN_FULL_MILLIS = "yyyy-MM-dd HH:mm:ss:SSS";
-
- public static String formatDate (long timestamp, int utcOffset) {
- return DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_FULL_MILLIS).format(LocalDateTime.ofInstant(
- Instant.ofEpochMilli(timestamp),
- ZoneId.ofOffset("UTC", ZoneOffset.ofHours(utcOffset))
- ));
- }
-
- public static String formatDuration (long duration) {
- StringBuilder sb = new StringBuilder();
- if (duration > 1000 * 60 * 60 * 24) sb.append(duration / (1000*60*60*24)).append("d ");
- if (duration > 1000 * 60 * 60) sb.append(duration / (1000*60*60) % 24).append("h ");
- if (duration > 1000 * 60) sb.append(duration / (1000*60) % 60).append("min ");
- if (duration > 1000) sb.append(duration / 1000 % 60).append("s ");
- sb.append(duration % 1000).append("ms");
- return sb.toString();
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java b/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java
deleted file mode 100644
index 57ff657..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import javax.annotation.Nonnull;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class FileUtils {
-
- @Nonnull
- public static String getMD5Three (@Nonnull String path) throws IOException, NoSuchAlgorithmException {
- final BigInteger bi;
- final byte[] buffer = new byte[8192];
- int len;
- final MessageDigest md = MessageDigest.getInstance("MD5");
- final FileInputStream fis = new FileInputStream(path);
- while ((len = fis.read(buffer)) != -1) {
- md.update(buffer, 0, len);
- }
- fis.close();
- final byte[] b = md.digest();
- bi = new BigInteger(1, b);
- return bi.toString(16);
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/UniversalCommand.java b/src/main/java/cc/sukazyo/cono/morny/util/UniversalCommand.java
deleted file mode 100644
index f9e11b8..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/UniversalCommand.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import javax.annotation.Nonnull;
-import java.util.ArrayList;
-
-public class UniversalCommand {
-
- @Nonnull
- public static String[] format (@Nonnull String com) {
-
- final ArrayList
+ * 不设定的话,默认将会使用 {@code https://api.telegram.org/bot}
+ */
+ @Nullable public final String telegramBotApiServer;
+ /**
+ * Morny Telegram 使用的 API 服务器的 file 服务路径.
+ *
+ * 不设定的话,默认将会使用 {@value com.pengrad.telegrambot.impl.FileApi#FILE_API}
+ */
+ @Nullable public final String telegramBotApiServer4File;
+
+ /**
+ * morny 使用的 telegram bot 的 bot api token.
+ *
+ * 这个值必须设定。
+ */
+ @Nonnull @Sensitive public final String telegramBotKey;
+ /**
+ * morny 所使用的 bot 的 username.
+ *
+ * 如果设定了这个值,则在 morny 登录 bot 时将会检查所登录的 bot 的 username 是否和这里设定的 username 匹配。
+ * 如果不匹配,则会拒绝登录然后报错。
+ *
+ * 如果没有设定这个值,则不会对登录 bot 的 username 进行限制。
+ */
+ @Nullable public final String telegramBotUsername;
+
+ /* ======================================= *
+ * morny trusted config *
+ * ======================================= */
+
+ /**
+ * morny 的主人.
+ *
+ * 这项值的对象总是会被{@link MornyTrusted 信任管理器}认为是可信任的
+ */
+ public final long trustedMaster;
+ /**
+ * morny 可信群聊的 id.
+ *
+ * {@link MornyTrusted 信任管理器}将会认为这个群聊中的所有拥有
+ * {@link com.pengrad.telegrambot.model.ChatMember.Status#administrator administrator} 权限的成员是可信任的。
+ *
+ * id 需要符合 bot api 标准。
+ */
+ public final long trustedChat;
+
+ /* ======================================= *
+ * system: event ignore *
+ * ======================================= */
+
+ public final boolean eventIgnoreOutdated;
+ /**
+ * morny 的事件忽略前缀时间
- *
- * 会反复尝试三次进行登录。如果登录失败,则会直接抛出 RuntimeException 结束处理。
- * 会通过 GetMe 动作验证是否连接上了 telegram api 服务器,
- * 同时也要求登录获得的 username 和 {@link #username} 声明值相等
- *
- * @param api bot client 将会连接到的 telegram bot api 位置
- * @param api4File bot client 将会连接到的 telegram file api 位置,如果不指定则会跟随 {@code api} 选项的设定
- * @param key bot 的 api-token
- * @param requireName 要求登录到的需要的 username,如果登陆后的 username 与此不同则会报错退出
- * @return 成功登录后的 {@link TelegramBot} 对象
- */
- @Nonnull
- private static LogInResult login (
- @Nullable String api, @Nullable String api4File,
- @Nonnull String key, @Nullable String requireName
- ) {
- final TelegramBot.Builder accountConfig = new TelegramBot.Builder(key);
- boolean isCustomApi = false;
- String apiUrlSet = "https://api.telegram.org/bot";
- String api4FileUrlSet = FileApi.FILE_API;
- if (api != null) {
- api = api.endsWith("/") ? api.substring(0, api.length() - 1) : api;
- accountConfig.apiUrl(apiUrlSet = api.endsWith("/bot")? api : api + "/bot");
- isCustomApi = true;
- }
- if (api4File != null) {
- api4File = api4File.endsWith("/") ? api4File : api4File + "/";
- accountConfig.fileApiUrl(api4FileUrlSet = api4File.endsWith("/file/bot")? api4File : api4File + "/file/bot");
- isCustomApi = true;
- } else if (api != null && !api.endsWith("/bot")) {
- accountConfig.fileApiUrl(api4FileUrlSet = api + "/file/bot");
- }
- if (isCustomApi) {
- logger.info(String.format("""
- Telegram Bot API set to :
- - %s
- - %s""",
- apiUrlSet, api4FileUrlSet
- ));
- }
- final TelegramBot account = accountConfig.build();
- logger.info("Trying to login...");
- for (int i = 1; i < 4; i++) {
- if (i != 1) logger.info("retrying...");
- try {
- final User remote = account.execute(new GetMe()).user();
- if (requireName != null && !requireName.equals(remote.username()))
- throw new RuntimeException("Required the bot @" + requireName + " but @" + remote.username() + " logged in!");
- logger.info("Succeed login to @" + remote.username());
- return new LogInResult(account, remote.username(), remote.id());
- } catch (Exception e) {
- e.printStackTrace(System.out);
- logger.error("login failed.");
- }
- }
- throw new RuntimeException("Login failed..");
- }
-
- /**
- * @see #saveDataAll()
- * @since 0.4.3.0
- */
- public static void callSaveData () {
- INSTANCE.saveDataAll();
- logger.info("done all save action.");
- }
-
- /**
- * 获取登录成功后的 telegram bot 对象
- *
- * @return {@link #account MornyCoeur.account}
- */
- @Nonnull
- public static TelegramBot getAccount () {
- return INSTANCE.account;
- }
-
- /**
- * 获取登录 bot 的 username
- *
- * @return {@link #username MornyCoeur.username}
- */
- @Nonnull
- public static String getUsername () {
- 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;
- }
-
- @Nonnull
- public static MornyCommands commandManager () {
- return INSTANCE.commandManager;
- }
-
- @Nonnull
- public static MornyQueries queryManager () {
- return INSTANCE.queryManager;
- }
-
- @Nonnull
- public static ExtraAction extra () {
- return INSTANCE.extraActionInstance;
- }
-
- public static long getUserid () { return INSTANCE.userid; }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyHello.java b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java
deleted file mode 100644
index 8a3725b..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/MornyHello.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package cc.sukazyo.cono.morny;
-
-/**
- * {@link #MORNY_PREVIEW_IMAGE_ASCII} 静态数据存放类
- */
-@SuppressWarnings("all")
-public class MornyHello {
-
- /**
- * 系统的开屏欢迎语 ASCII 字符画字段
- */
- public static final String MORNY_PREVIEW_IMAGE_ASCII = """
- ttt///t/////fucj(\\tvnxtf{< .' .. .:i` . . ^!`l|-^i+,!_[:1/|{i?//\\//jf\\\\\\///\\\\\\\\//\\\\\\//////\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\/\\\\//\\\\\\///\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\fnncvvU0O00QCx!!". .. ` \s
- tt//////////\\jzjrucnjt/?{j,,"' . .' .. .":. .;{: ' "`.,1(<."i?)\\(-}\\\\\\(((\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\//////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\///\\\\///\\\\\\\\\\\\\\\\|\\\\\\\\\\\\|\\\\\\\\\\\\\\\\tvXvuXcxn/[Il)({_:.. ."` .,\s
- //////////////////\\////|)/([}-_<+[]>.^^""[<'`^` .''""`'.`'`"i! ^!>l:' :<" !!.IiI`+l^^`i>_<`??)1;^{\\\\\\\\\\{|({({|/\\]I)\\\\()\\(]}|\\\\||\\|||\\/\\\\\\\\\\\\|||\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\||\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\\\////\\\\\\\\\\\\\\\\\\//|{{?{|)[[-;
- ttt/tt//////////////////{)(\\t(/tt/1~I}{-1\\_^])1_+[{|(?"<1~>>+!+[}11)}[(1}]};^1\\|~_1}{I:-1(I+)(|))|\\\\/////////\\\\////\\\\\\/////\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\//\\\\///\\//||\\////|)(//\\\\///){\\/\\(11|///({)//({[1\\\\\\\\\\\\\\\\\\\\|\\/\\\\\\/\\//////////\\\\\\\\\\\\\\\\\\//\\\\\\\\\\///////\\|\\\\\\\\//////\\\\///\\
- tttt/////////////\\///////\\||///////t//|(|)|}|\\/(\\\\(//(l_{{. ... ">+<^'I!: ^<(\\\\1}1//\\\\\\//////////\\\\///\\/\\///\\\\\\\\\\\\//\\\\//\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\(/\\{
- t////////////////////////////////////////////////////////\\/\\\\///////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\||\\|\\\\\\\\\\|\\\\\\\\/\\\\|\\\\\\\\\\\\////((|///}!:,":,^`. .;' ' '^..':. ^!;. .^^ '^^`. '' ...I[{!>:^;_i:'~\\ttt/////tt//\\\\////////////\\\\\\\\/\\\\\\\\\\\\/\\\\\\\\/\\\\\\\\\\)}-+[+I??i
- ttt////////////////////////////////////////////////////////\\\\//\\//\\\\/\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\||\\\\\\/\\//\\\\\\\\\\\\\\\\\\/\\|\\\\\\////\\1;``^;<>+!">__+I `' .. "'. .;" ;;. .:^ ``,,;'` .;]I ,-_-|\\////t////t///////\\/\\\\\\\\//\\\\\\\\\\\\//\\\\\\///////-II1ttt///tttt/////////\\/\\/////\\\\\\\\////t|+<}?!-]l<{[[1-+]
- t//////////////////////////////////////\\/////////\\////////////////\\//////\\//tttttttttt//////////////////////////)_)t)|}1f/{<.^,^:~: . .. '''^:-|/> '-/}-_?\\/)-{?(//\\(\\tt////\\///\\\\\\\\\\\\\\///t1.;); .l~` '"
- ///////////////////////////////////////////tt/t(|tt//]+{t\\{][|////\\//////////ttttt///t//t/////////////\\//////|//{[|f}!l>~++~<<\\//]l~?])tt//\\\\\\\\\\/\\\\///\\\\|?<_}["^!;I^;]:. .
- ////////////////////////////////tttt/|{[1)]~!!+>!<_(/|[-<"i!l,]tt//ttt/t////ttt//t///ttttttt////////t//t//ttt){+. :?^ '. l_-!+l;;;|!!>~~il!lllllllllll!!lI:`'. .' :I;]_}>,?tf:.+fft)l+1//\\~`'I-(//\\/t/|/(-1[)/?>>II:' '.`';-'` \s
- /////////////////////////t//()\\1_<>il^'''' ,!>;.,.'{tti `~tf(`'-(|fffftttttttttt/tttttttttttt///tttft//(t|]?-+!^ ."`. `. ;!I,. .?{il-\\_!~<>>!lII;IllIIIIIllllllllllI;;:,:,' '"^`(f{+{>' .<{t(I!}/||t> ^(//}>;:1\\]: "[:"` ^<: . II.'.. \s
- ///////////////ttt//tt((-!+}"'^. I, ,?<:' ,:;!>~',!_~{}-1]`^!}_+\\ttttttt/tttttttfff/tt\\(||]-?+;,:"l" '..'.. ?]l:" -(lI;,~?~!IIIlllI:IIlI;IIIIlllllllIIIIIllII!; . . '^^;~), "~!}\\/t//\\\\/_. '||1?-)/\\\\)+_1>". '_i !i''' \s
- tt//t///ttt///(]<>l>][l"'.`,. ^.^. ii ;; ~>>>. .i~I'^^<}), .;|tfftttttttttttf\\]}t-!,,I` .^ '. !: . .",I;. ^,I<)/-l:;llllllI;lIll;;IIIIlllIllIlIIlIllI;><. ' .;}". '.:+](ft\\}(t/t{;<\\{l^>}!^l\\/{>1/t(lI:I!+<<". ':" \s
- t//tttt|?+!I!:' '` .`. ...... `^ "<^.;`^"'`,!".,^^^.,?)!. [f/+>(/tttft\\tff|+^,!' '^: >[,++:`' .I^ . _?!:^. ;~{/
- * 会由 gradle 任务 {@code updateVersionCode} 更新
- */
- public static final String VERSION = GradleProjectConfigures.VERSION;
-
- /**
- * Morny Coeur 当前的版本代号.
- * 一个单个单词,一般作为一个大版本的名称,只在重大更新改变
- * 格式保持为仅由小写字母和数字组成
- * 有时也可能是复合词或特殊的词句
- *
- * 会由 gradle 任务 {@code updateVersionCode} 更新
- */
- public static final String CODENAME = GradleProjectConfigures.CODENAME;
-
- /**
- * 获取程序 jar 文件的 md5-hash 值
- *
- * 只支持 jar 文件方式启动的程序 ——
- * 如果是通过 classpath 来启动,程序无法找到本体jar文件,则会返回 {@code
- * 值格式为 {@link java.lang.String}
- *
- * @return 程序jar文件的 md5-hash 值字符串,或 {@code
- * 这项值的对象总是会被认为是可信任的
- */
- public final long MASTER;
-
- private final Set
- *
- * 用户需要受信任才能执行一些对程序甚至是宿主环境而言危险的操作,例如关闭程序
- *
- * 它的逻辑(目前)是检查群聊 {@link #TRUSTED_CHAT_ID} 中这个用户是否为群组管理员
- *
- * @param userId 需要检查的用户的id
- * @return 所传递的用户id对应的用户是否受信任
- */
- public boolean isTrusted (long userId) {
- if (userId == MASTER) return true;
- return MornyCoeur.extra().isUserInGroup(userId, TRUSTED_CHAT_ID, Status.administrator);
- }
-
- public boolean isTrustedForDinnerRead (long userId) {
- return TRUSTED_READERS_OF_DINNER.contains(userId);
- }
-
- public Set
- *
- * 会处理程序传入的参数和选项等数据,并执行对应的启动方式
- *
- * @since 0.4.0.0
- */
-public class ServerMain {
-
- public static final String PROP_TOKEN_KEY = "TELEGRAM_BOT_API_TOKEN";
- public static final String PROP_TOKEN_MORNY_KEY = "MORNY_TG_TOKEN";
-
- private static final String THREAD_MORNY_INIT = "morny-init";
-
- /**
- * 程序入口,也是参数处理器
- *
- * 以 {@code -} 开头的参数会被解析为选项
- *
- * 支持以下选项
- *
- *
- *
- * 用于 bot 启动的 telegram bot api token
- * 除去选项之外,第一个参数会被赋值为 bot 的 telegram bot api token,
- * 第二个参数会被赋值为 bot 的 username 限定名。其余的参数会被认定为无法理解。
- * 自 {@code 0.4.2.3},token 和 username 的赋值已被选项组支持
- * 自 {@code 0.5.0.4},旧的直接通过参数为 bot token & username 赋值的方式已被删除
- * 使用参数所进行取值的 token 和 username 已被转移至 {@code --token} 和 {@code --username} 参数
- *
- * @see MornyCoeur#main
- * @since 0.4.0.0
- * @param args 参数组
- */
- public static void main (@Nonnull String[] args) {
-
- //#
- //# 启动参数设置区块
- //#
-
- boolean versionEchoMode = false;
- boolean welcomeEchoMode = false;
- boolean showWelcome = true;
- String key = null;
- String username = null;
- boolean outdatedBlock = false;
- long master = 793274677L;
- Set
"
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- } else {
- MornyCoeur.extra().exec(new SendDocument(
- event.message().chat().id(),
- result
- ).fileName(resultName).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java
deleted file mode 100644
index ddbe9e2..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.MornyTrusted;
-import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * {@link OnEventHackHandle} 的命令行前端
- * @since 0.4.2.0
- */
-public class EventHack implements ITelegramCommand {
-
- @Nonnull @Override public String getName () { return "event_hack"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return "[(user|group|any)]"; }
- @Nonnull @Override public String getDescription () { return "输出 bot 下一个获取到的事件序列化数据"; }
-
- /**
- * {@link OnEventHackHandle} 的命令行前端" + MsgEscape.escapeHtml(resultString) + "
- *
- * 实现了通过命令行进行 EventHack 功能。
- * 支持三种模式,默认为 {@link OnEventHackHandle.HackType#USER USER},
- * {@link OnEventHackHandle.HackType#ANY ANY} 时,将会通过 {@link MornyTrusted#isTrusted(long)} 检查触发用户的权限
- *
- * @param event 命令基础参数,触发的事件对象本身
- * @param command 命令基础参数,解析出的命令对象
- * @since 0.4.2.0
- */
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- 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.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_WAITING
- ).replyToMessageId(event.message().messageId())
- );
- } else {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_403
- ).replyToMessageId(event.message().messageId())
- );
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java
deleted file mode 100644
index bcc2bcf..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.User;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.GetChatMember;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.response.GetChatMemberResponse;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class GetUsernameAndId implements ITelegramCommand {
-
- @Nonnull @Override public String getName () { return "user"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return "[userid]"; }
- @Nonnull @Override public String getDescription () { return "获取指定或回复的用户相关信息"; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- final String[] args = command.getArgs();
-
- if (args.length > 1) { MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] Too much arguments."
- ).replyToMessageId(event.message().messageId())); return; }
-
- long userId = event.message().from().id();
-
- if (event.message().replyToMessage()!= null) {
- userId = event.message().replyToMessage().from().id();
- }
- if (args.length > 0) {
- try {
- userId = Long.parseLong(args[0]);
- } catch (NumberFormatException e) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] " + e.getMessage()
- ).replyToMessageId(event.message().messageId()));
- return;
- }
- }
-
- final GetChatMemberResponse response = MornyCoeur.getAccount().execute(
- new GetChatMember(event.message().chat().id(), userId)
- );
-
- if (response.chatMember() == null) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] user not found."
- ).replyToMessageId(event.message().messageId()));
- return;
- }
-
- final User user = response.chatMember().user();
-
- if (user.id() == 136817688) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "$__channel_identify
"
- ));
- return;
- }
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- TelegramUserInformation.informationOutputHTML(user)
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.java
deleted file mode 100644
index 453a97e..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public interface ISimpleCommand {
-
- @Nonnull
- String getName();
-
- @Nullable
- String[] getAliases();
-
- void execute (@Nonnull InputCommand command, @Nonnull Update event);
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.java
deleted file mode 100644
index b090a95..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import javax.annotation.Nonnull;
-
-public interface ITelegramCommand extends ISimpleCommand {
-
- @Nonnull
- String getParamRule();
- @Nonnull
- String getDescription();
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java
deleted file mode 100644
index 212305a..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse;
-import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-import org.jetbrains.annotations.NotNull;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-
-/**
- * {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端
- * @since 0.4.2.10
- */
-public class Ip186Query {
-
- public static final String CMD_IP = "ip";
- public static final String CMD_WHOIS = "whois";
-
- public static class Ip implements ITelegramCommand {
- @Nonnull @Override public String getName () { return CMD_IP; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Nonnull @Override public String getParamRule () { return "[ip]"; }
- @Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询 ip 资料"; }
- @Override public void execute (@NotNull InputCommand command, @NotNull Update event) { exec(event, command); }
- }
-
- public static class Whois implements ITelegramCommand {
- @Nonnull @Override public String getName () { return CMD_WHOIS; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Nonnull @Override public String getParamRule () { return "[domain]"; }
- @Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询域名资料"; }
- @Override public void execute (@NotNull InputCommand command, @NotNull Update event) { exec(event, command); }
- }
-
- private static void exec (@Nonnull Update event, @Nonnull InputCommand command) {
-
- String arg = null;
- if (!command.hasArgs()) {
- if (event.message().replyToMessage() != null) {
- arg = event.message().replyToMessage().text();
- }
- } else if (command.getArgs().length > 1) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] Too much arguments."
- ).replyToMessageId(event.message().messageId()));
- return;
- } else {
- arg = command.getArgs()[0];
- }
- if (arg == null) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] No ip defined."
- ).replyToMessageId(event.message().messageId()));
- return;
- }
-
- try {
- IP186QueryResponse response = switch (command.getCommand()) {
- case CMD_IP -> IP186QueryHandler.queryIp(arg);
- case CMD_WHOIS -> IP186QueryHandler.queryWhoisPretty(arg);
- default -> throw new IllegalArgumentException("Unknown 186-IP query method " + command.getCommand());
- };
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- escapeHtml(response.url()) + "\n" + escapeHtml(response.body()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- } catch (Exception e) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Exception] in query:\n" + escapeHtml(e.getMessage()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java
deleted file mode 100644
index 7b37258..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java
+++ /dev/null
@@ -1,373 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.GradleProjectConfigures;
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.MornySystem;
-import cc.sukazyo.cono.morny.data.MornyJrrp;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
-import com.pengrad.telegrambot.model.BotCommand;
-import com.pengrad.telegrambot.model.DeleteMyCommands;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-import com.pengrad.telegrambot.request.SetMyCommands;
-import org.jetbrains.annotations.NotNull;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import static cc.sukazyo.cono.morny.Log.logger;
-import static cc.sukazyo.cono.morny.util.CommonFormat.formatDate;
-import static cc.sukazyo.cono.morny.util.CommonFormat.formatDuration;
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-public class MornyCommands {
-
- private final Map%s
- - %s
- core md5_hash:
- - %s
- compile timestamp:
- - %d
- - %s [UTC]
""",
- escapeHtml(MornySystem.CODENAME.toUpperCase()),
- escapeHtml(MornySystem.VERSION),
- escapeHtml(MornySystem.getJarMd5()),
- GradleProjectConfigures.COMPILE_TIMESTAMP,
- escapeHtml(formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0))
- )
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- }
-
- private static class MornyRuntime implements ITelegramCommand {
- @Nonnull @Override public String getName () { return "runtime"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return ""; }
- @Nonnull @Override public String getDescription () { return "获取 Bot 运行时信息(包括版本号)"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandRuntimeExec(event); }
- }
- /**
- * @since 0.4.1.2
- */
- private static void onCommandRuntimeExec (@Nonnull Update event) {
- String hostname;
- try {
- hostname = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- hostname = "%s
- - %s
- - %s
- java runtime:
- - %s
- - %s
- vm memory:
- - %d
/ %d
MB
- - %d
cores
- coeur version:
- - %s
(%s
)
- - %s
- - %s [UTC]
- - [%d
]
- continuous:
- - %s
- - [%d
]
- - %s [UTC]
- - [%d
]""",
- // system
- escapeHtml(hostname),
- escapeHtml(String.format("%s (%s)", System.getProperty("os.name"), System.getProperty("os.arch"))),
- escapeHtml(System.getProperty("os.version")),
- // java
- escapeHtml(System.getProperty("java.vm.vendor")+"."+System.getProperty("java.vm.name")),
- escapeHtml(System.getProperty("java.vm.version")),
- // memory
- Runtime.getRuntime().totalMemory() / 1024 / 1024,
- Runtime.getRuntime().maxMemory() / 1024 / 1024,
- Runtime.getRuntime().availableProcessors(),
- // version
- escapeHtml(MornySystem.VERSION),
- escapeHtml(MornySystem.CODENAME),
- escapeHtml(MornySystem.getJarMd5()),
- escapeHtml(formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)),
- GradleProjectConfigures.COMPILE_TIMESTAMP,
- // continuous
- escapeHtml(formatDuration(System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp)),
- System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp,
- escapeHtml(formatDate(MornyCoeur.coeurStartTimestamp, 0)),
- MornyCoeur.coeurStartTimestamp
- )
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- }
-
- private static class Jrrp implements ITelegramCommand {
- @Nonnull @Override public String getName () { return "jrrp"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return ""; }
- @Nonnull @Override public String getDescription () { return "获取 (假的) jrrp"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandJrrpExec(event); }
- }
- private static void onCommandJrrpExec (Update event) {
- final double jrrp = MornyJrrp.getJrrpFromTelegramUser(event.message().from(), System.currentTimeMillis());
- final String endChar = jrrp>70 ? "!" : jrrp>30 ? ";" : "...";
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- String.format(
- "%s 在(utc的)今天的运气指数是———— %.2f%%
%s",
- TGToString.as(event.message().from()).fullnameRefHtml(),
- jrrp, escapeHtml(endChar)
- )
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- }
-
- private static class SaveData implements ITelegramCommand {
- @Nonnull @Override public String getName () { return "save"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return ""; }
- @Nonnull @Override public String getDescription () { return "保存缓存数据到文件(仅可信成员)"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onSaveDataExec(event); }
- }
- /**
- * @since 0.4.3.0
- */
- private static void onSaveDataExec (Update event) {
- if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
- logger.info("called save from command by " + TGToString.as(event.message().from()).toStringLogTag());
- MornyCoeur.callSaveData();
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_SAVED
- ).replyToMessageId(event.message().messageId())
- );
- } else {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_403
- ).replyToMessageId(event.message().messageId())
- );
- logger.info("403 call save tag from user " + TGToString.as(event.message().from()).toStringLogTag());
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java
deleted file mode 100644
index 9d815bb..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class MornyInformations implements ITelegramCommand {
-
- private static final String ACT_STICKER = "stickers";
-
- @Nonnull @Override public String getName () { return "info"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Nonnull @Override public String getParamRule () { return "[(stickers)|(stickers.)sticker_id]"; }
- @Nonnull @Override public String getDescription () { return "输出 Morny 当前版本的一些预定义信息"; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- if (!command.hasArgs() || command.getArgs().length > 1) {
- MornyCoeur.extra().exec(new SendSticker(event.message().chat().id(), TelegramStickers.ID_404).replyToMessageId(event.message().messageId()));
- }
-
- final String action = command.getArgs()[0];
-
- if (action.startsWith("stickers")) {
- if (action.equals("stickers"))
- TelegramStickers.echoAllStickers(MornyCoeur.extra(), event.message().chat().id(), event.message().messageId());
- else {
- TelegramStickers.echoStickerByID(
- action.substring((ACT_STICKER+".").length()),
- MornyCoeur.extra(), event.message().chat().id(), event.message().messageId()
- );
- }
- return;
- }
-
- MornyCoeur.extra().exec(new SendSticker(event.message().chat().id(), TelegramStickers.ID_404).replyToMessageId(event.message().messageId()));
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java
deleted file mode 100644
index 22e87cc..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.NbnhhshQuery;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting;
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-public class Nbnhhsh implements ITelegramCommand {
-
- @Nonnull @Override public String getName () { return "nbnhhsh"; }
- @Nullable @Override public String[] getAliases () { return null; }
- @Nonnull @Override public String getParamRule () { return "[text]"; }
- @Nonnull @Override public String getDescription () { return "检索文本内 nbnhhsh 词条"; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- try {
-
- String queryTarget = "";
- if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null)
- queryTarget = event.message().replyToMessage().text();
- if (command.hasArgs())
- queryTarget = stringsConnecting(command.getArgs(), " ", 0, command.getArgs().length-1);
-
- NbnhhshQuery.GuessResult response = NbnhhshQuery.sendGuess(queryTarget);
-
- StringBuilder message = new StringBuilder("## Result of nbnhhsh query :");
-
- for (NbnhhshQuery.Word word : response.words) {
- if (word.trans != null && word.trans.length == 0) word.trans = null;
- if (word.inputting != null && word.inputting.length == 0) word.inputting = null;
- if (word.trans == null && word.inputting == null) continue;
- message.append("\n\n[[ ").append(escapeHtml(word.name)).append(" ]]");
- if (word.trans != null) for (String trans : word.trans) {
- message.append("\n* ").append(escapeHtml(trans)).append("");
- }
- if (word.inputting != null) {
- if (word.trans != null) message.append("\n");
- message.append(" maybe:");
- for (String trans : word.inputting) {
- message.append("\n` ").append(escapeHtml(trans)).append("");
- }
- }
- }
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- message.toString()
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
-
- } catch (Exception e) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Exception] in query:\n" + escapeHtml(e.getMessage()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Roll.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Roll.java
deleted file mode 100644
index c3c865f..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Roll.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-public class Roll {
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java
deleted file mode 100644
index d1bfb80..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class Testing implements ISimpleCommand {
-
- @Nonnull
- @Override
- public String getName () {
- return "test";
- }
-
- @Nullable
- @Override
- public String[] getAliases () {
- return null;
- }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "Just a TEST command."
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java
deleted file mode 100644
index 2bdbb7d..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-@SuppressWarnings("NonAsciiCharacters")
-public class 喵呜 {
-
- public static class 抱抱 implements ISimpleCommand {
- @Nonnull @Override public String getName () { return "抱抱"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "抱抱——"
- ));
- }
- }
-
- public static class 揉揉 implements ISimpleCommand {
- @Nonnull @Override public String getName () { return "揉揉"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "蹭蹭w"
- ));
- }
- }
-
- public static class 蹭蹭 implements ISimpleCommand {
- @Nonnull @Override public String getName () { return "蹭蹭"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "喵呜~-"
- ));
- }
- }
-
- public static class 贴贴 implements ISimpleCommand {
- @Nonnull @Override public String getName () { return "贴贴"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "
- * 跟随 {@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 事件基础返回值,是否已完成处理事件:
- * 如果匹配到呼叫,则返回{@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);
- case "dinner", "lunch", "breakfast", "meal", "eating", "安妮今天吃什么" ->
- requestLastDinner(update);
- default -> {
- if (update.message().text().startsWith("cc::")) {
- requestCustomCall(update);
- break;
- }
- return false;
- }
- }
- MornyCoeur.extra().exec(new SendSticker(
- update.message().chat().id(),
- TelegramStickers.ID_SENT
- ).replyToMessageId(update.message().messageId())
- );
- return true;
- }
-
- /**
- * 执行 steam library 呼叫
- * 将会向 {@link #ME} 发送
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestSteamJoin (Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request STEAM LIBRARY
- from %s""",
- TGToString.as(event.message().from()).fullnameRefHtml()
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 执行花宫呼叫
- * 将会向 {@link #ME} 发送
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestHanaParesuJoin (Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request Hana Paresu
- from %s""",
- TGToString.as(event.message().from()).fullnameRefHtml()
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 对访问最近一次的饭局的请求进行回复
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestLastDinner (Update event) {
- boolean isAllowed = false;
- Message lastDinnerData = null;
- if (MornyCoeur.trustedInstance().isTrustedForDinnerRead(event.message().from().id())) {
- lastDinnerData = MornyCoeur.extra().exec(new GetChat(MornyCoeur.DINNER_CHAT_ID)).chat().pinnedMessage();
- SendResponse sendResp = MornyCoeur.extra().exec(new ForwardMessage(
- event.message().from().id(),
- lastDinnerData.forwardFromChat().id(),
- lastDinnerData.forwardFromMessageId()
- ));
- MornyCoeur.extra().exec(new SendMessage(
- event.message().from().id(),
- String.format("on %s [UTC+8]
\n- %s
before",
- MsgEscape.escapeHtml(
- CommonFormat.formatDate((long)lastDinnerData.forwardDate()*1000, 8)
- ), MsgEscape.escapeHtml(
- CommonFormat.formatDuration(System.currentTimeMillis()-(long)lastDinnerData.forwardDate()*1000)
- )
- )
- ).replyToMessageId(sendResp.message().messageId()).parseMode(ParseMode.HTML));
- isAllowed = true;
- } else {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().from().id(),
- TelegramStickers.ID_403
- ).replyToMessageId(event.message().messageId()));
- }
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request Last Annie Dinner
- from %s
- %s""",
- TGToString.as(event.message().from()).fullnameRefHtml(),
- isAllowed ? "Allowed and returned " + String.format(
- "https://t.me/c/%d/%d", Math.abs(lastDinnerData.forwardFromChat().id()+1000000000000L), lastDinnerData.forwardFromMessageId()
- ) : "Forbidden by perm check."
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 执行自定义呼叫
- * 将会向 {@link #ME} 发送一个 request 数据消息和转发的原始请求消息
- *
- * known issue
- *
- *
- * 现在你可以通过这个 bot 来呼叫主人(sukazyo)任何事情了 ——
- * 但是直接私聊sukazyo不好吗
- *
- * @param event 执行呼叫的tg事件
- * @since 0.4.2.2
- */
- private static void requestCustomCall (Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request [???]
- from %s""",
- TGToString.as(event.message().from()).fullnameRefHtml()
- )
- ).parseMode(ParseMode.HTML));
- MornyCoeur.extra().exec(new ForwardMessage(
- ME,
- event.message().chat().id(),
- event.message().messageId()
- ));
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java
deleted file mode 100644
index d02beae..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package cc.sukazyo.cono.morny.bot.event;
-
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import com.pengrad.telegrambot.model.Chat;
-import com.pengrad.telegrambot.model.Message;
-import com.pengrad.telegrambot.model.MessageEntity;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.GetChat;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.bot.api.EventListener;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import com.pengrad.telegrambot.response.GetChatResponse;
-import com.pengrad.telegrambot.response.SendResponse;
-
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-
-public class OnCallMsgSend extends EventListener {
-
- private static final Pattern REGEX_MSG_SENDREQ_DATA_HEAD = Pattern.compile("^\\*msg([\\d-]+)(\\*\\S+)?\\n([\\s\\S]+)$");
-
- private record MessageToSend (
- String message,
- MessageEntity[] entities,
- ParseMode parseMode,
- long targetId
- ) { }
-
- @Override
- public boolean onMessage(Update update) {
-
- // 执行体检查
- if (update.message().chat().type() != Chat.Type.Private) return false;
- if (update.message().text() == null) return false;
- if (!update.message().text().startsWith("*msg")) return false;
-
- // 权限检查
- if (!MornyCoeur.trustedInstance().isTrusted(update.message().from().id())) {
- MornyCoeur.extra().exec(new SendSticker(
- update.message().chat().id(),
- TelegramStickers.ID_403
- ).replyToMessageId(update.message().messageId()));
- return true;
- }
-
- Message msgsendReqRaw; // 用户书写的发送请求原文
- MessageToSend msgsendReqBody; // 解析后的发送请求实例
-
- // *msgsend 发送标识
- // 处理发送要求
- if (update.message().text().equals("*msgsend")) {
- // 发送体处理
- if (update.message().replyToMessage() == null) return answer404(update);
- msgsendReqBody = parseRequest(update.message().replyToMessage());
- if (msgsendReqBody == null) return answer404(update);
- // 执行发送任务
- SendResponse sendResponse = MornyCoeur.getAccount().execute(parseMessageToSend(msgsendReqBody));
- if (!sendResponse.isOk()) { // 发送失败
- MornyCoeur.extra().exec(new SendMessage(
- update.message().chat().id(),
- String.format("""
- %d FAILED
- %s
""",
- sendResponse.errorCode(),
- sendResponse.description()
- )
- ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
- } else { // 发送成功信号
- MornyCoeur.extra().exec(new SendSticker(
- update.message().chat().id(),
- TelegramStickers.ID_SENT
- ).replyToMessageId(update.message().messageId()));
- }
- return true;
- // 发送完成/失败 - 事件结束
- }
-
- // *msg 检查标识
- if (update.message().text().equals("*msg")) { // 处理对曾经的原文的检查
- if (update.message().replyToMessage() == null) {
- return answer404(update);
- }
- msgsendReqRaw = update.message().replyToMessage();
- } else if (update.message().text().startsWith("*msg")) { // 对接受到的原文进行检查
- msgsendReqRaw = update.message();
- } else {
- return answer404(update); // 未定义的动作
- }
-
- // 对发送请求的用户原文进行解析
- msgsendReqBody = parseRequest(msgsendReqRaw);
- if (msgsendReqBody == null) {
- return answer404(update);
- }
-
- // 输出发送目标信息
- GetChatResponse targetChatReq = MornyCoeur.getAccount().execute(new GetChat(msgsendReqBody.targetId()));
- if (!targetChatReq.isOk()) {
- MornyCoeur.extra().exec(new SendMessage(
- update.message().chat().id(),
- String.format("""
- %d FAILED
- %s
""",
- targetChatReq.errorCode(),
- targetChatReq.description()
- )
- ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
- } else {
- MornyCoeur.extra().exec(new SendMessage(
- update.message().chat().id(),
- targetChatReq.chat().type() == Chat.Type.Private ? (
- String.format("""
- %d@%s
- 🔒 %s %s""",
- msgsendReqBody.targetId(),
- escapeHtml(targetChatReq.chat().type().name()),
- escapeHtml(targetChatReq.chat().firstName()+(targetChatReq.chat().lastName()==null?"":" "+targetChatReq.chat().lastName())),
- targetChatReq.chat().username()==null?
- String.format("@@", targetChatReq.chat().id()):
- (escapeHtml("@"+targetChatReq.chat().username()))
- )
- ) : (
- String.format("""
- %d@%s:::
- %s %s%s""",
- msgsendReqBody.targetId(),
- escapeHtml(targetChatReq.chat().type().name()),
- switch (targetChatReq.chat().type()) {
- case group -> "💭";
- case channel -> "📢";
- case supergroup -> "💬";
- default -> "⭕️";
- },
- escapeHtml(targetChatReq.chat().title()),
- targetChatReq.chat().username() != null?String.format(
- " @%s", escapeHtml(targetChatReq.chat().username())
- ):""
- )
- )
- ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
- }
- // 发送文本测试
- SendResponse testSendResp = MornyCoeur.getAccount().execute(
- parseMessageToSend(msgsendReqBody, update.message().chat().id()).replyToMessageId(update.message().messageId())
- );
- if (!testSendResp.isOk()) {
- MornyCoeur.extra().exec(new SendMessage(
- update.message().chat().id(),
- String.format("""
- %d FAILED
- %s
""",
- testSendResp.errorCode(),
- testSendResp.description()
- )
- ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
- }
-
- return true;
-
- }
-
- @Nullable
- private static MessageToSend parseRequest (@Nonnull Message requestBody) {
-
- final Matcher matcher = REGEX_MSG_SENDREQ_DATA_HEAD.matcher(requestBody.text());
- if (matcher.matches()) {
- long targetId = Long.parseLong(matcher.group(1));
- ParseMode parseMode = matcher.group(2) == null ? null : switch (matcher.group(2)) {
- case "*markdown", "*md", "*m↓" -> ParseMode.MarkdownV2;
- case "*md1" -> ParseMode.Markdown;
- case "*html" -> ParseMode.HTML;
- default -> null;
- };
- final int offset = "*msg".length()+matcher.group(1).length()+(matcher.group(2)==null?0:matcher.group(2).length())+1;
- final ArrayList%s
",
- MsgEscape.escapeHtml(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().chat().id());
- }
-
- @Override
- public boolean onEditedChannelPost (@Nonnull Update update) {
- return onEventHacked(update, update.editedChannelPost().chat().id(), update.editedChannelPost().chat().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());
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQueries.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQueries.java
deleted file mode 100644
index ed06f0b..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQueries.java
+++ /dev/null
@@ -1,47 +0,0 @@
-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.bot.api.InlineQueryUnit;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.InlineQueryResult;
-import com.pengrad.telegrambot.request.AnswerInlineQuery;
-
-import javax.annotation.Nonnull;
-import java.util.List;
-
-/**
- * telegram inlineQuery 功能的处理类,
- * 也是一个 InlineQueryManager(还没做)
- *
- * @since 0.4.1.3
- */
-public class OnInlineQueries extends EventListener {
-
- /**
- * @since 0.4.1.3
- */
- @Override
- public boolean onInlineQuery (@Nonnull Update update) {
-
- List
- *
- * @see #isOutdated 时间判断
- */
-public class OnUpdateTimestampOffsetLock extends EventListener {
-
- /**
- * 检查传入时间是否在要求时间之前(即"过期").
- * @param timestamp 传入时间,秒级
- * @return 如果传入时间在要求时间之前,返回true,反之false
- * @since 0.4.2.7
- */
- public boolean isOutdated(long timestamp) {
- return timestamp < MornyCoeur.getLatestEventTimestamp()/1000;
- }
-
- @Override
- public boolean onMessage (@Nonnull Update update) {
- 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());
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java
deleted file mode 100644
index 898bdac..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java
+++ /dev/null
@@ -1,52 +0,0 @@
-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.UniversalCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.request.SendMessage;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class OnUserRandoms extends EventListener {
-
- private static final Pattern USER_OR_CN_QUERY = Pattern.compile("(.+)还是(.+)");
- private static final Pattern USER_OR_EN_QUERY = Pattern.compile("(.+)or(.+)");
-
- @Override
- public boolean onMessage (@NotNull Update update) {
-
- if (update.message().text() == null) return false;
- if (!update.message().text().startsWith("/")) return false;
-
- final String[] preProcess = UniversalCommand.format(update.message().text());
- if (preProcess.length > 1) return false;
- final String query = preProcess[0];
-
- // ----- START CODE BLOCK COMMENT -----
- // 这里实现思路和代码优化有至少一半是 copilot 和 IDEA 提供的
- // 实现思路都可以从人类手里抢一半贡献太恐怖了aba
- String result = null;
- final Matcher matcher;
- if (query.contains("还是")) {
- matcher = USER_OR_CN_QUERY.matcher(query);
- } else {
- matcher = USER_OR_EN_QUERY.matcher(query);
- }
- if (matcher.find()) {
- result = ThreadLocalRandom.current().nextBoolean() ? matcher.group(1) : matcher.group(2);
- }
- // ----- STOP CODE BLOCK COMMENT -----
-
- if (result == null) return false;
- MornyCoeur.extra().exec(new SendMessage(
- update.message().chat().id(), result
- ).replyToMessageId(update.message().messageId()));
- return true;
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java
deleted file mode 100644
index cf1910b..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java
+++ /dev/null
@@ -1,84 +0,0 @@
-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.UniversalCommand;
-import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
-
-import com.pengrad.telegrambot.model.Message;
-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.CommonConvert.stringsConnecting;
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-public class OnUserSlashAction extends EventListener {
-
- @Override
- public boolean onMessage (@Nonnull Update event) {
- final String text = event.message().text();
- if (text == null) return false;
-
- if (text.startsWith("/"))
- {
-
- /// Due to @Lapis_Apple, we stopped slash action function at .DP7 groups.
- /// It may be enabled after some updates when the function will not be conflicted to other bots.
- // if (event.message().chat().id() == ) return false;
-//{ if (event.message().chat().title() != null && event.message().chat().title().contains(".DP7")) {
-// logger.info(String.format("""
-// Chat slash action ignored due to the following keyword.
-// - %s
-// - ".DP7\"""",
-// TGToString.as(event.message().chat()).toStringFullNameId()
-// ));
-// return false;
-// }
-
- final String[] action = UniversalCommand.format(text);
- action[0] = action[0].substring(1);
-
- if (action[0].matches("^\\w+(@\\w+)?$")) {
- return false; // 忽略掉 Telegram 命令格式的输入
- } else if (action[0].contains("/")) {
- return false; // 忽略掉疑似目录格式的输入
- }
-
- final boolean isHardParse = "".equals(action[0]);
- /* 忽略空数据 */ if (isHardParse && action.length < 2) { return false; }
- final String verb = isHardParse ? action[1] : action[0];
- final boolean hasObject = action.length != (isHardParse?2:1);
- final String object =
- hasObject ?
- stringsConnecting(action, " ", isHardParse?2:1, action.length-1) :
- "";
- final Message origin = event.message();
- final Message target = (event.message().replyToMessage() == null ? (
- origin
- ): (
- event.message().replyToMessage()
- ));
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- String.format(
- "%s %s%s %s %s!",
- TGToString.as(origin).getSenderFirstNameRefHtml(),
- escapeHtml(verb), escapeHtml((hasObject?"":"了")),
- origin==target ?
- "自己" :
- TGToString.as(target).getSenderFirstNameRefHtml(),
- escapeHtml(hasObject ? object+" " : "")
- )
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
-
- return true;
-
- }
- return false;
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.java b/src/main/java/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.java
deleted file mode 100644
index c79ab37..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package cc.sukazyo.cono.morny.bot.query;
-
-import javax.annotation.Nullable;
-
-import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
-import com.pengrad.telegrambot.model.Update;
-
-import java.util.List;
-
-public interface ITelegramQuery {
-
- @Nullable
- Listuid@daystamp
这样的字符串,
- * 然后通过 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(CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(userId + "@" + dayStamp)).substring(0, 4), 16) / (double)0xffff;
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/data/NbnhhshQuery.java b/src/main/java/cc/sukazyo/cono/morny/data/NbnhhshQuery.java
deleted file mode 100644
index cd1be74..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/data/NbnhhshQuery.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cc.sukazyo.cono.morny.data;
-
-import java.io.IOException;
-
-import com.google.gson.Gson;
-
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-
-public class NbnhhshQuery {
-
- public static class Word {
- public String name;
- public String[] trans;
- public String[] inputting;
- }
-
- public static class GuessResult {
- public Word[] words;
- }
-
- public record GuessReq (String text) {
- }
-
- public static final String API_URL = "https://lab.magiconch.com/api/nbnhhsh/";
- public static final String API_GUESS_METHOD = "guess/";
- public static final String API_GUESS_DATA_TEMPLATE = "{ \"text\": \"%s\" }";
-
- private static final OkHttpClient httpClient = new OkHttpClient();
- public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
-
- public static GuessResult sendGuess (String text) throws IOException {
- final String reqJsonText = new Gson().toJson(new GuessReq(text));
- Request request = new Request.Builder()
- .url(API_URL + API_GUESS_METHOD)
- .post(RequestBody.create(JSON, reqJsonText))
- .build();
- try (Response response = httpClient.newCall(request).execute()) {
- final ResponseBody body = response.body();
- if (body == null) throw new IOException("Null body.");
- final String x = "{ \"words\": " + body.string() + " }";
- return new Gson().fromJson(x, GuessResult.class);
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.java b/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.java
deleted file mode 100644
index 0f60d81..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.java
+++ /dev/null
@@ -1,89 +0,0 @@
-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 参数.
- * 目的使 API 直接返回原始数据
- */
- private static final String QUERY_IP_PARAM = "type=json&format=true";
-
- /**
- * 进行 {@link #queryWhois whois 查询}时所使用的 API 参数.
- * 目的使 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);
- }
-
- /**
- * 将 {@link #queryWhois(String)} 的结果进行裁剪.
- *
- * 将会删除返回内容中 {@code >>> XXX <<<} 行以后的注释串,
- * 以达到只保留重要信息的目的。
- *
- * @see #queryWhois(String)
- */
- @Nonnull
- public static IP186QueryResponse queryWhoisPretty (String domain) throws IOException {
- final IP186QueryResponse raw = queryWhois(domain);
- return new IP186QueryResponse(raw.url(), raw.body().substring(0, raw.body().indexOf("<<<")+3));
- }
-
- @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());
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryResponse.java b/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryResponse.java
deleted file mode 100644
index f30fb3d..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/data/ip186/IP186QueryResponse.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package cc.sukazyo.cono.morny.data.ip186;
-
-/**
- * {@link IP186QueryHandler} 的请求结果数据的通用封装类.
- *
- * @since 0.4.2.10
- * @param url 请求数据的人类可读的来源链接,并非api链接
- * @param body API 传回的数据内容
- */
-public record IP186QueryResponse(String url, String body) {
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/BiliTool.java b/src/main/java/cc/sukazyo/cono/morny/util/BiliTool.java
deleted file mode 100644
index ef36993..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/util/BiliTool.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import javax.annotation.Nonnegative;
-import javax.annotation.Nonnull;
-import java.util.HashMap;
-import java.util.Map;
-
-public class BiliTool {
-
- private static final long V_CONV_XOR = 177451812L;
- private static final long V_CONV_ADD = 8728348608L;
- private static final char[] BV_TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF".toCharArray();
- private static final int TABLE_INT = BV_TABLE.length;
- private static final Map
- * eg:
- * while the link {@code https://www.bilibili.com/video/BV17x411w7KC/}
- * shows the same with {@code https://www.bilibili.com/video/av170001/},
- * the AV id is {@code 170001}, the BV id is {@code 17x411w7KC}
- *
- * eg:
- * while the link {@code https://www.bilibili.com/video/BV17x411w7KC/}
- * shows the same with {@code https://www.bilibili.com/video/av170001/},
- * the AV id is {@code 170001}, the BV id is {@code 17x411w7KC}
- * %d
""",
- user.id()
- ));
- if (user.username() == null) {
- userInformation.append("\nusername : null\ndatacenter : null");
- } else {
- userInformation.append(String.format(
- """
-
- username :
- - %s
""",
- escapeHtml(user.username())
- ));
- // 依赖 username 的 datacenter 查询
- final String dataCenter = getDataCenterFromUsername(user.username());
- if (dataCenter == null) { userInformation.append("\ndatacenter : null"); }
- else { userInformation.append(String.format("\ndatacenter : %s
", escapeHtml(dataCenter))); }
- }
- userInformation.append(String.format(
- """
-
- display name :
- - %s
%s""",
- escapeHtml(user.firstName()),
- user.lastName()==null ? "" : String.format("\n- %s
", escapeHtml(user.lastName()))
- ));
- if (user.languageCode() != null) {
- userInformation.append(String.format(
- """
-
- language-code :
- - %s
""",
- escapeHtml(user.languageCode())
- ));
- }
-
- return userInformation.toString();
-
- }
-
-}
diff --git a/src/main/resources/assets_morny/images/featured-image@0.5x.jpg b/src/main/resources/assets_morny/images/featured-image@0.5x.jpg
new file mode 100644
index 0000000..444cf8f
Binary files /dev/null and b/src/main/resources/assets_morny/images/featured-image@0.5x.jpg differ
diff --git a/src/main/resources/assets_morny/texts/server-hello.txt b/src/main/resources/assets_morny/texts/server-hello.txt
new file mode 100644
index 0000000..96136ec
--- /dev/null
+++ b/src/main/resources/assets_morny/texts/server-hello.txt
@@ -0,0 +1,55 @@
+ttt///t/////fucj(\tvnxtf{< .' .. .:i` . . ^!`l|-^i+,!_[:1/|{i?//\//jf\\\///\\\\//\\\//////\\/\\\\\\\\\\\\\\//\\\\/\\\\/\\//\\\///\\\\\\\\\\\\\\\\\\\\fnncvvU0O00QCx!!". .. `
+tt//////////\jzjrucnjt/?{j,,"' . .' .. .":. .;{: ' "`.,1(<."i?)\(-}\\\(((\\/\\\\\\\\\\\\\\\\///\//////\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\///\\///\\\\\\\\|\\\\\\|\\\\\\\\tvXvuXcxn/[Il)({_:.. ."` .,
+//////////////////\////|)/([}-_<+[]>.^^""[<'`^` .''""`'.`'`"i! ^!>l:' :<" !!.IiI`+l^^`i>_<`??)1;^{\\\\\{|({({|/\]I)\\()\(]}|\\||\|||\/\\\\\\|||\\\\\\\\//\\\\/\\\\||\\\\\\\\//\\\\\\\\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\////\\\\\\\\\//|{{?{|)[[-;
+ttt/tt//////////////////{)(\t(/tt/1~I}{-1\_^])1_+[{|(?"<1~>>+!+[}11)}[(1}]};^1\|~_1}{I:-1(I+)(|))|\\/////////\\////\\\/////\\\\\\\\\\\\\\/\//\\///\//||\////|)(//\\///){\/\(11|///({)//({[1\\\\\\\\\\|\/\\\/\//////////\\\\\\\\\//\\\\\///////\|\\\\//////\\///\
+tttt/////////////\///////\||///////t//|(|)|}|\/(\\(//(l_{{. ... ">+<^'I!: ^<(\\1}1//\\\//////////\\///\/\///\\\\\\//\\//\\\\\\\\\\\\\\///\(/\{
+t////////////////////////////////////////////////////////\/\\///////\\\\\\\\\\\\\\\/\\\\\\||\|\\\\\|\\\\/\\|\\\\\\////((|///}!:,":,^`. .;' ' '^..':. ^!;. .^^ '^^`. '' ...I[{!>:^;_i:'~\ttt/////tt//\\////////////\\\\/\\\\\\/\\\\/\\\\\)}-+[+I??i
+ttt////////////////////////////////////////////////////////\\//\//\\/\\\\\\\\\\//\\\\\\||\\\/\//\\\\\\\\\/\|\\\////\1;``^;<>+!">__+I `' .. "'. .;" ;;. .:^ ``,,;'` .;]I ,-_-|\////t////t///////\/\\\\//\\\\\\//\\\///////-II1ttt///tttt/////////\/\/////\\\\////t|+<}?!-]l<{[[1-+]
+t//////////////////////////////////////\/////////\////////////////\//////\//tttttttttt//////////////////////////)_)t)|}1f/{<.^,^:~: . .. '''^:-|/> '-/}-_?\/)-{?(//\(\tt////\///\\\\\\\///t1.;); .l~` '"
+///////////////////////////////////////////tt/t(|tt//]+{t\{][|////\//////////ttttt///t//t/////////////\//////|//{[|f}!l>~++~<<\//]l~?])tt//\\\\\/\\///\\|?<_}["^!;I^;]:. .
+////////////////////////////////tttt/|{[1)]~!!+>!<_(/|[-<"i!l,]tt//ttt/t////ttt//t///ttttttt////////t//t//ttt){+. :?^ '. l_-!+l;;;|!!>~~il!lllllllllll!!lI:`'. .' :I;]_}>,?tf:.+fft)l+1//\~`'I-(//\/t/|/(-1[)/?>>II:' '.`';-'`
+/////////////////////////t//()\1_<>il^'''' ,!>;.,.'{tti `~tf(`'-(|fffftttttttttt/tttttttttttt///tttft//(t|]?-+!^ ."`. `. ;!I,. .?{il-\_!~<>>!lII;IllIIIIIllllllllllI;;:,:,' '"^`(f{+{>' .<{t(I!}/||t> ^(//}>;:1\]: "[:"` ^<: . II.'..
+///////////////ttt//tt((-!+}"'^. I, ,?<:' ,:;!>~',!_~{}-1]`^!}_+\ttttttt/tttttttfff/tt\(||]-?+;,:"l" '..'.. ?]l:" -(lI;,~?~!IIIlllI:IIlI;IIIIlllllllIIIIIllII!; . . '^^;~), "~!}\/t//\\/_. '||1?-)/\\)+_1>". '_i !i'''
+tt//t///ttt///(]<>l>][l"'.`,. ^.^. ii ;; ~>>>. .i~I'^^<}), .;|tfftttttttttttf\]}t-!,,I` .^ '. !: . .",I;. ^,I<)/-l:;llllllI;lIll;;IIIIlllIllIlIIlIllI;><. ' .;}". '.:+](ft\}(t/t{;<\{l^>}!^l\/{>1/t(lI:I!+<<". ':"
+t//tttt|?+!I!:' '` .`. ...... `^ "<^.;`^"'`,!".,^^^.,?)!. [f/+>(/tttft\tff|+^,!' '^: >[,++:`' .I^ . _?!:^. ;~{/
+ *
+ * {@link cc.sukazyo.cono.morny.bot.event.MornyOnUpdateTimestampOffsetLock}
+ * 会根据这里定义的时间戳取消掉比此时间更早的事件链
+ */
+ public final long eventOutdatedTimestamp;
+
+ /* ======================================= *
+ * system: command list automation *
+ * ======================================= */
+
+ public final boolean commandLoginRefresh;
+ public final boolean commandLogoutClear;
+
+ /* ======================================= *
+ * system: morny report *
+ * ======================================= */
+
+ /**
+ * 控制 Morny Coeur 系统的报告的报告对象.
+ * @since 1.0.0-alpha5
+ */
+ public final long reportToChat;
+
+ /* ======================================= *
+ * function: dinner query tool *
+ * ======================================= */
+
+ @Nonnull public final Set
"
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ }
+
+ /** echo help to a specific message in a specific chat.
+ *
+ * === the help message ===
+ * The first paragraph lists available encrypt algorithms and its alias,
+ * each line have one algorithm where the first name highlighted is the
+ * main name and following is aliases separated with `,`.
+ * with the separator `---`, the second paragraph lists available mods
+ * for algorithms, displays with the same rule of algorithms, with an extra
+ * italic text following describes its usage environment.
+ *
+ * when output to telegram just like:
+ * ${h(_text.text)}
+ * '''__base64__''', b64
+ */
+ private def echoHelp(chat: Long, replyTo: Int): Unit =
+ coeur.account exec SendMessage(
+ chat,
+ s"""base64, b64
+ |base64url, base64u, b64u
+ |base64decode, base64d, b64d
+ |base64url-decode, base64ud, b64ud
+ |sha1
+ |sha256
+ |sha512
+ |md5
+ |---
+ |uppercase, upper, u (sha1/sha256/sha512/md5 only)"""
+ .stripMargin
+ ).replyToMessageId(replyTo).parseMode(ParseMode HTML)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala
new file mode 100644
index 0000000..03211e8
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala
@@ -0,0 +1,57 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendSticker
+
+import scala.language.postfixOps
+
+class EventHack (using coeur: MornyCoeur) extends ITelegramCommand {
+
+ override val name: String = "event_hack"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override val paramRule: String = "[(user|group|any)]"
+ override val description: String = "输出 bot 下一个获取到的事件序列化数据"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ import coeur.daemons.eventHack.{registerHack, HackType}
+
+ val x_mode = if (command.args nonEmpty) command.args(0) else ""
+
+ def done_ok =
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_WAITING
+ ).replyToMessageId(event.message.messageId)
+ def done_forbiddenForAny =
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(event.message.messageId)
+
+ def doRegister (t: HackType): Unit =
+ registerHack(
+ event.message.messageId longValue,
+ event.message.from.id,
+ event.message.chat.id,
+ t
+ )
+ x_mode match
+ case "any" =>
+ if (coeur.trusted isTrusted event.message.from.id)
+ doRegister(HackType ANY)
+ done_ok
+ else done_forbiddenForAny
+ case "group" =>
+ doRegister(HackType GROUP)
+ done_ok
+ case _ =>
+ doRegister(HackType USER)
+ done_ok
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala
new file mode 100644
index 0000000..a960c07
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala
@@ -0,0 +1,68 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.{InputCommand, Standardize}
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
+
+import scala.language.postfixOps
+
+class GetUsernameAndId (using coeur: MornyCoeur) extends ITelegramCommand {
+
+ override val name: String = "user"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override val paramRule: String = "[userid]"
+ override val description: String = "获取指定或回复的用户相关信息"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ val args = command.args
+
+ if (args.length > 1)
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] Too much arguments."
+ ).replyToMessageId(event.message.messageId)
+ return
+
+ val userId: Long =
+ if (args nonEmpty) {
+ try args(0) toLong
+ catch case e: NumberFormatException =>
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ s"[Unavailable] ${e.getMessage}"
+ ).replyToMessageId(event.message.messageId)
+ return
+ } else if (event.message.replyToMessage eq null) event.message.from.id
+ else event.message.replyToMessage.from.id
+
+ val response = coeur.account execute GetChatMember(event.message.chat.id, userId)
+
+ if (response.chatMember eq null)
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] user not found."
+ ).replyToMessageId(event.message.messageId)
+ return
+
+ val user = response.chatMember.user
+
+ if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID)
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ "
+ * '''__base64url__''', base64u, b64u
+ * '''__base64decode__''', base64d, b64d
+ * '''__base64url-decode__''', base64ud, b64ud
+ * '''__sha1__'''
+ * '''__sha256__'''
+ * '''__sha512__'''
+ * '''__md5__'''
+ * ---
+ * '''__uppercase__''', upper, u ''(sha1/sha256/sha512/md5 only)''
+ * $__channel_identify
"
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ TelegramUserInformation getFormattedInformation user
+ ).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/ICommandAlias.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ICommandAlias.scala
new file mode 100644
index 0000000..de9dbe7
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ICommandAlias.scala
@@ -0,0 +1,44 @@
+package cc.sukazyo.cono.morny.bot.command
+
+/** One alias definition, contains the necessary message of how
+ * to process the alias.
+ */
+trait ICommandAlias {
+
+ /** The alias name.
+ *
+ * same with the command name, it is the unique identifier of this alias.
+ */
+ val name: String
+ /** If the alias should be listed while list commands to end-user.
+ *
+ * The alias can only be listed when the parent command can be listed
+ * (meanwhile the parent command implemented [[ITelegramCommand]]). If the
+ * parent command cannot be listed, it will always cannot be listed.
+ */
+ val listed: Boolean
+
+}
+
+/** Default implementations of [[ICommandAlias]]. */
+object ICommandAlias {
+
+ /** Alias which can be listed to end-user.
+ *
+ * the [[ICommandAlias.listed]] value is always true.
+ *
+ * @param name The alias name, see more in [[ICommandAlias.name]]
+ */
+ case class ListedAlias (name: String) extends ICommandAlias:
+ override val listed = true
+
+ /** Alias which cannot be listed to end-user.
+ *
+ * the [[ICommandAlias.listed]] value is always false.
+ *
+ * @param name The alias name, see more in [[ICommandAlias.name]]
+ */
+ case class HiddenAlias (name: String) extends ICommandAlias:
+ override val listed = false
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala
new file mode 100644
index 0000000..60d53a7
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala
@@ -0,0 +1,78 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+class IP186Query (using coeur: MornyCoeur) {
+
+ private enum Subs (val cmd: String):
+ case IP extends Subs("ip")
+ case WHOIS extends Subs("whois")
+
+ object IP extends ITelegramCommand:
+ override val name: String = "ip"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override val paramRule: String = "[ip]"
+ override val description: String = "通过 https://ip.186526.xyz 查询 ip 资料"
+ override def execute (using command: InputCommand, event: Update): Unit = query
+ object Whois extends ITelegramCommand:
+ override val name: String = "whois"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override val paramRule: String = "[domain]"
+ override val description: String = "通过 https://ip.186526.xyz 查询域名资料"
+ override def execute (using command: InputCommand, event: Update): Unit = query
+
+ private def query (using event: Update, command: InputCommand): Unit = {
+
+ val target: String|Null =
+ if (command.args isEmpty)
+ if event.message.replyToMessage eq null then null else event.message.replyToMessage.text
+ else if (command.args.length > 1)
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] Too much arguments."
+ ).replyToMessageId(event.message.messageId)
+ return
+ else command.args(0)
+
+ if (target eq null)
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ "[Unavailable] No ip defined."
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+ try {
+
+ val response = command.command match
+ case Subs.IP.cmd => IP186QueryHandler.query_ip(target)
+ case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target)
+ case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}")
+
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ s"""${h(response.url)}
+ |${h(response.body)}
"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ } catch case e: Exception =>
+ coeur.account exec new SendMessage(
+ event.message().chat().id(),
+ s"""[Exception] in query:
+ |${h(e.getMessage)}
"""
+ .stripMargin
+ ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId())
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.scala
new file mode 100644
index 0000000..24c624f
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ISimpleCommand.scala
@@ -0,0 +1,39 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+
+/** A simple command.
+ *
+ * Contains only [[name]] and [[aliases]].
+ *
+ * Won't be listed to end-user. if you want the command listed,
+ * see [[ITelegramCommand]].
+ *
+ */
+trait ISimpleCommand {
+
+ /** the main name of the command.
+ *
+ * must have a value as the unique identifier of this command.
+ */
+ val name: String
+ /** aliases of the command.
+ *
+ * Alias means it is the same to call [[name main name]] when call this.
+ * There can be multiple aliases. But notice that, although alias is not
+ * the unique identifier, it uses the same namespace with [[name]], means
+ * it also cannot be duplicate with other [[name]] or [[aliases]].
+ *
+ * It can be [[Null]], means no aliases.
+ */
+ val aliases: Array[ICommandAlias]|Null
+
+ /** The work code of this command.
+ *
+ * @param command The parsed input command which called this command.
+ * @param event The raw event which called this command.
+ */
+ def execute (using command: InputCommand, event: Update): Unit
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.scala
new file mode 100644
index 0000000..1a720b8
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/ITelegramCommand.scala
@@ -0,0 +1,25 @@
+package cc.sukazyo.cono.morny.bot.command
+
+/** A complex telegram command.
+ *
+ * the extension of [[ISimpleCommand]], with external defines of the necessary
+ * introduction message ([[paramRule]] and [[description]]).
+ *
+ * It can be listed to end-user.
+ */
+trait ITelegramCommand extends ISimpleCommand {
+
+ /** The param rule of this command, used in human-readable command list.
+ *
+ * The param rule uses a symbol language to describe how this command
+ * receives paras.
+ *
+ * Set it empty to make this scope not available.
+ */
+ val paramRule: String
+ /** The description/introduction of this command, used in human-readable
+ * command list.
+ */
+ val description: String
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala
new file mode 100644
index 0000000..dbc3205
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala
@@ -0,0 +1,123 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.{BotCommand, DeleteMyCommands, Update}
+import com.pengrad.telegrambot.request.{SendSticker, SetMyCommands}
+
+import scala.collection.{mutable, SeqMap}
+import scala.collection.mutable.ArrayBuffer
+import scala.language.postfixOps
+
+class MornyCommands (using coeur: MornyCoeur) {
+
+ private type CommandMap = SeqMap[String, ISimpleCommand]
+ private def CommandMap (commands: ISimpleCommand*): CommandMap =
+ val stash = mutable.SeqMap.empty[String, ISimpleCommand]
+ for (i <- commands)
+ stash += (i.name -> i)
+ if (i.aliases ne null) for (alias <- i.aliases)
+ stash += (alias.name -> i)
+ stash
+
+ private val $MornyHellos = MornyHellos()
+ private val $IP186Query = IP186Query()
+ private val $MornyInformation = MornyInformation()
+ private val $MornyInformationOlds = MornyInformationOlds(using $MornyInformation)
+ private val $MornyManagers = MornyManagers()
+ //noinspection NonAsciiCharacters
+ private val $喵呜 = 喵呜()
+ private val commands: CommandMap = CommandMap(
+
+ $MornyHellos.On,
+ $MornyHellos.Hello,
+ MornyInfoOnStart(),
+ GetUsernameAndId(),
+ EventHack(),
+ Nbnhhsh(),
+ $IP186Query.IP,
+ $IP186Query.Whois,
+ Encryptor(),
+ $MornyManagers.SaveData,
+ $MornyInformation,
+ $MornyInformationOlds.Version,
+ $MornyInformationOlds.Runtime,
+ MornyOldJrrp(),
+ $MornyManagers.Exit,
+
+ Testing(),
+ DirectMsgClear(),
+
+ //noinspection NonAsciiCharacters
+ 私わね(),
+ //noinspection NonAsciiCharacters
+ $喵呜.Progynova
+
+ )
+
+ //noinspection NonAsciiCharacters
+ val commands_uni: CommandMap = CommandMap(
+ $喵呜.抱抱,
+ $喵呜.揉揉,
+ $喵呜.贴贴,
+ $喵呜.蹭蹭
+ )
+
+ def execute (using command: InputCommand, event: Update): Boolean = {
+ if (commands contains command.command)
+ commands(command.command) execute;
+ true
+ else nonCommandExecutable
+ }
+
+ private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = {
+ if command.target eq null then false
+ else
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(event.message.messageId)
+ true
+ }
+
+ def automaticTGListUpdate (): Unit = {
+ val listing = commands_toTelegramList
+ automaticTGListRemove()
+ coeur.account exec SetMyCommands(listing:_*)
+ logger info
+ s"""automatic updated telegram command list :
+ |${commandsTelegramList_toString(listing)}""".stripMargin
+ }
+
+ def automaticTGListRemove (): Unit = {
+ coeur.account exec DeleteMyCommands()
+ logger info "cleaned up command list"
+ }
+
+ private def commandsTelegramList_toString (list: Array[BotCommand]): String =
+ val builder = StringBuilder()
+ for (single <- list)
+ builder ++= s"${single.command} - ${single.description}\n"
+ (builder dropRight 1) toString
+
+ private def commands_toTelegramList: Array[BotCommand] =
+ val list = ArrayBuffer.empty[BotCommand]
+ for ((name, command) <- commands) command match
+ case telegramCommand: ITelegramCommand if name == command.name =>
+ list ++= formatTelegramCommandListLine(telegramCommand)
+ case _ =>
+ list toArray
+
+ private def formatTelegramCommandListLine (command: ITelegramCommand): Array[BotCommand] =
+ def buildOne (name: String, paramRule: String, intro: String): BotCommand =
+ BotCommand(name, if paramRule isBlank then intro else s"$paramRule - $intro")
+ val list = mutable.ArrayBuffer[BotCommand](
+ buildOne(command.name, command.paramRule, command.description))
+ if (command.aliases ne null) for (alias <- command.aliases)
+ if (alias.listed) list += buildOne(alias.name, "", "↑")
+ list toArray
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala
new file mode 100644
index 0000000..34d8943
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala
@@ -0,0 +1,44 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.command.ICommandAlias.ListedAlias
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendSticker
+
+import scala.language.postfixOps
+
+class MornyHellos (using coeur: MornyCoeur) {
+
+ object On extends ITelegramCommand {
+
+ override val name: String = "on"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override val paramRule: String = ""
+ override val description: String = "检查是否在线"
+
+ override def execute (using command: InputCommand, event: Update): Unit =
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_ONLINE_STATUS_RETURN
+ ).replyToMessageId(event.message.messageId)
+
+ }
+
+ object Hello extends ITelegramCommand {
+
+ override val name: String = "hello"
+ override val aliases: Array[ICommandAlias] | Null = Array(ListedAlias("hi"))
+ override val paramRule: String = ""
+ override val description: String = "打招呼"
+
+ override def execute (using command: InputCommand, event: Update): Unit =
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_HELLO
+ ).replyToMessageId(event.message.messageId)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala
new file mode 100644
index 0000000..db0bd7e
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala
@@ -0,0 +1,37 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML}
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendPhoto
+
+import scala.language.postfixOps
+
+class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand {
+
+ override val name: String = "start"
+ override val aliases: Array[ICommandAlias] | Null = null
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ coeur.account exec new SendPhoto(
+ event.message.chat.id,
+ getAboutPic
+ ).caption(
+ s"""欢迎使用 Morny Cono,来自安妮的侍从小鼠。
+ |Morny 具有各种各样的功能。
+ |
+ |————————————————
+ |$getMornyAboutLinksHTML
+ |————————————————
+ |
+ |(你可以随时通过 /info 重新获得这些信息)"""
+ .stripMargin
+ ).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala
new file mode 100644
index 0000000..36222eb
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala
@@ -0,0 +1,153 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.{BuildConfig, MornyCoeur, MornySystem}
+import cc.sukazyo.cono.morny.data.MornyInformation.*
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker}
+
+import java.lang.System
+import scala.language.postfixOps
+
+// todo: maybe move some utils method outside
+class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
+
+ private case object Subs {
+ val STICKERS = "stickers"
+ val RUNTIME = "runtime"
+ val VERSION = "version"
+ val VERSION_2 = "v"
+ }
+
+ override val name: String = "info"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override val paramRule: String = "[(version|runtime|stickers[.IDs])]"
+ override val description: String = "输出当前 Morny 的各种信息"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ if (command.args isEmpty) {
+ echoInfo(event.message.chat.id, event.message.messageId)
+ return
+ }
+
+ val action: String = command.args(0)
+
+ action match {
+ case s if s startsWith Subs.STICKERS => echoStickers
+ case Subs.RUNTIME => echoRuntime
+ case Subs.VERSION | Subs.VERSION_2 => echoVersion
+ case _ => echo404
+ }
+
+ }
+
+ private def echoInfo (chatId: Long, replyTo: Int): Unit = {
+ coeur.account exec new SendPhoto(
+ chatId,
+ getAboutPic
+ ).caption(
+ s"""Morny Cono
+ |来自安妮的侍从小鼠。
+ |————————————————
+ |$getMornyAboutLinksHTML"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(replyTo)
+ }
+
+ private def echoStickers (using command: InputCommand, event: Update): Unit = {
+ val mid: String|Null =
+ if (command.args(0) == Subs.STICKERS) {
+ if (command.args.length == 1) ""
+ else if (command.args.length == 2) command.args(1)
+ else null
+ } else if (command.args.length == 1) {
+ if ((command.args(0) startsWith s"${Subs.STICKERS}.") || (command.args(0) startsWith s"${Subs.STICKERS}#")) {
+ command.args(0) substring Subs.STICKERS.length+1
+ } else null
+ } else null
+ if (mid == null) echo404
+ else echoStickers(mid)(using event.message.chat.id, event.message.messageId)
+ }
+
+ private def echoStickers (mid: String)(using send_chat: Long, send_replyTo: Int)(using Update): Unit = {
+ import scala.jdk.CollectionConverters.*
+ if (mid isEmpty) for ((_key, _file_id) <- TelegramStickers.map asScala)
+ echoSticker(_key, _file_id)
+ else {
+ try {
+ val sticker = TelegramStickers getById mid
+ echoSticker(sticker.getKey, sticker.getValue)
+ } catch case _: NoSuchFieldException => {
+ echo404
+ }
+ }
+ }
+
+ private def echoSticker (mid: String, file_id: String)(using send_chat: Long, send_replyTo: Int): Unit = {
+ val send_mid = SendMessage(send_chat, mid)
+ val send_sticker = SendSticker(send_chat, file_id)
+ if (send_replyTo != -1) send_mid.replyToMessageId(send_replyTo)
+ val result_send_mid = coeur.account exec send_mid
+ send_sticker.replyToMessageId(result_send_mid.message.messageId)
+ coeur.account exec send_sticker
+ }
+
+ private[command] def echoVersion (using event: Update): Unit = {
+ val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ${h(MornySystem.VERSION_DELTA)}
" else ""
+ val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else ""
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ // language=html
+ s"""version:
+ |- Morny ${h(MornySystem.CODENAME toUpperCase)}
+ |- ${h(MornySystem.VERSION_BASE)}
$versionDeltaHTML${if (MornySystem.isGitBuild) "\n- " + versionGitHTML else ""}
+ |coeur md5_hash:
+ |- ${h(MornySystem.getJarMD5)}
+ |coding timestamp:
+ |- ${BuildConfig.CODE_TIMESTAMP}
+ |- ${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]
+ |""".stripMargin
+ ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
+ }
+
+ private[command] def echoRuntime (using event: Update): Unit = {
+ def sysprop (p: String): String = System.getProperty(p)
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ /* language=html */
+ s"""system:
+ |- ${h(if getRuntimeHostname nonEmpty then getRuntimeHostname.get else "
+ |- ${h(sysprop("os.name"))}
${h(sysprop("os.arch"))}
${h(sysprop("os.version"))}
+ |java runtime:
+ |- ${h(sysprop("java.vm.vendor"))}.${h(sysprop("java.vm.name"))}
+ |- ${h(sysprop("java.vm.version"))}
+ |vm memory:
+ |- ${Runtime.getRuntime.totalMemory/1024/1024}
/ ${Runtime.getRuntime.maxMemory/1024/1024}
MB
+ |- ${Runtime.getRuntime.availableProcessors}
cores
+ |coeur version:
+ |- $getVersionAllFullTagHTML
+ |- ${h(MornySystem.getJarMD5)}
+ |- ${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]
+ |- [${BuildConfig.CODE_TIMESTAMP}
]
+ |continuous:
+ |- ${h(formatDuration(System.currentTimeMillis - coeur.coeurStartTimestamp))}
+ |- [${System.currentTimeMillis - coeur.coeurStartTimestamp}
]
+ |- ${h(formatDate(coeur.coeurStartTimestamp, 0))}
+ |- [${coeur.coeurStartTimestamp}
]"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+ }
+
+ private def echo404 (using event: Update): Unit =
+ coeur.account exec new SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(event.message.messageId)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala
new file mode 100644
index 0000000..60f94ec
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala
@@ -0,0 +1,18 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+
+class MornyInformationOlds (using base: MornyInformation) {
+
+ object Version extends ISimpleCommand:
+ override val name: String = "version"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override def execute (using command: InputCommand, event: Update): Unit = base.echoVersion
+
+ object Runtime extends ISimpleCommand:
+ override val name: String = "runtime"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override def execute (using command: InputCommand, event: Update): Unit = base.echoRuntime
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala
new file mode 100644
index 0000000..5a143c0
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala
@@ -0,0 +1,87 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.bot.command.ICommandAlias.HiddenAlias
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.daemon.MornyReport
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendSticker
+
+import scala.language.postfixOps
+
+class MornyManagers (using coeur: MornyCoeur) {
+
+ object Exit extends ITelegramCommand {
+
+ override val name: String = "exit"
+ override val aliases: Array[ICommandAlias] | Null = Array(HiddenAlias("stop"), HiddenAlias("quit"))
+ override val paramRule: String = "exit"
+ override val description: String = "关闭 Bot (仅可信成员)"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ val user = event.message.from
+
+ if (coeur.trusted isTrusted user.id) {
+
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_EXIT
+ ).replyToMessageId(event.message.messageId)
+ logger info s"Morny exited by user ${user toLogTag}"
+ coeur.exit(0, user)
+
+ } else {
+
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(event.message.messageId)
+ logger info s"403 exit caught from user ${user toLogTag}"
+ coeur.daemons.reporter.unauthenticatedAction("/exit", user)
+
+ }
+
+ }
+
+ }
+
+ object SaveData extends ITelegramCommand {
+
+ override val name: String = "save"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override val paramRule: String = ""
+ override val description: String = "保存缓存数据到文件(仅可信成员)"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ val user = event.message.from
+
+ if (coeur.trusted isTrusted user.id) {
+
+ logger info s"call save from command by ${user toLogTag}"
+ coeur.saveDataAll()
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_SAVED
+ ).replyToMessageId(event.message.messageId)
+
+ } else {
+
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(event.message.messageId)
+ logger info s"403 save caught from user ${user toLogTag}"
+ coeur.daemons.reporter.unauthenticatedAction("/save", user)
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala
new file mode 100644
index 0000000..2d44bff
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala
@@ -0,0 +1,36 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.data.MornyJrrp
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand {
+
+ override val name: String = "jrrp"
+ override val aliases: Array[ICommandAlias] | Null = null
+ override val paramRule: String = ""
+ override val description: String = "获取 (假的) jrrp"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ val user = event.message.from
+ val jrrp = MornyJrrp.jrrp_of_telegramUser(user, System.currentTimeMillis)
+ val ending = jrrp match
+ case s if s > 70 => "!"
+ case a if a > 30 => ";"
+ case _ => "..."
+
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ // language=html
+ f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— $jrrp%.2f%%
${h(ending)}"
+ ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala
new file mode 100644
index 0000000..543a783
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala
@@ -0,0 +1,85 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.{NbnhhshQuery, TelegramStickers}
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
+
+import java.io.IOException
+import scala.language.postfixOps
+
+class Nbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand {
+
+ private val NBNHHSH_RESULT_HEAD_HTML =
+ // language=html
+ "## Result of nbnhhsh query :"
+
+ override val name: String = "nbnhhsh"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override val paramRule: String = "[text]"
+ override val description: String = "检索文本内 nbnhhsh 词条"
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ val queryTarget: String =
+ if command.args nonEmpty then
+ command.args mkString " "
+ else if (event.message.replyToMessage != null && event.message.replyToMessage.text != null)
+ event.message.replyToMessage.text
+ else
+ coeur.account exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+ try {
+
+ val queryResp = NbnhhshQuery sendGuess queryTarget
+
+ val message = StringBuilder(NBNHHSH_RESULT_HEAD_HTML)
+
+ import cc.sukazyo.cono.morny.Log.logger
+ logger debug s"**xx len=${queryResp.words.length}"
+ for (_word <- queryResp.words) {
+ logger debug "**exec"
+ val _use_trans = (_word.trans ne null) && (_word.trans nonEmpty)
+ val _use_inputting = (_word.inputting ne null) && (_word.inputting nonEmpty)
+ if (_use_trans || _use_inputting)
+ message ++= s"\n\n[[ ${h(_word.name)} ]]"
+ logger debug s"**used [${_word.name}]"
+ if (_use_trans) for (_trans <- _word.trans)
+ message ++= s"\n* ${h(_trans)}"
+ logger debug s"**used [${_word.name}] used `${_trans}``"
+ if (_use_inputting)
+ logger debug s"**used [${_word.name}] inputting"
+ if (_use_trans)
+ message += '\n'
+ message ++= " maybe:"
+ for (_inputting <- _word.inputting)
+ logger debug s"**used [${_word.name}] used-i ${_inputting}"
+ message ++= s"\n` ${h(_inputting)}"
+ logger debug s"**exec as ${_word.name}"
+ }
+
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ message toString
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ } catch case e: IOException => {
+ coeur.account exec SendMessage(
+ event.message.chat.id,
+ s"""[Exception] in query:
+ |${h(e.getMessage)}
+ |""".stripMargin
+ )
+ }
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala
new file mode 100644
index 0000000..b7a1393
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala
@@ -0,0 +1,28 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import javax.annotation.{Nonnull, Nullable}
+import scala.language.postfixOps
+
+class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
+
+ override val name: String = "test"
+ override val aliases: Array[ICommandAlias] | Null = null
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ // language=html
+ "Just a TEST command."
+ ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala
new file mode 100644
index 0000000..996ab79
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala
@@ -0,0 +1,68 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.{Message, Update}
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
+
+import javax.swing.text.html.HTML
+import scala.annotation.unused
+import scala.language.postfixOps
+
+//noinspection NonAsciiCharacters
+class 喵呜 (using coeur: MornyCoeur) {
+
+ object 抱抱 extends ISimpleCommand {
+ override val name: String = "抱抱"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override def execute (using command: InputCommand, event: Update): Unit =
+ replyingSet("贴贴", "贴贴")
+ }
+
+ object 揉揉 extends ISimpleCommand {
+ override val name: String = "揉揉"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override def execute (using command: InputCommand, event: Update): Unit =
+ replyingSet("蹭蹭", "摸摸")
+ }
+
+ object 蹭蹭 extends ISimpleCommand {
+ override val name: String = "蹭蹭"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override def execute (using command: InputCommand, event: Update): Unit =
+ replyingSet("揉揉", "蹭蹭")
+ }
+
+ object 贴贴 extends ISimpleCommand {
+ override val name: String = "贴贴"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override def execute (using command: InputCommand, event: Update): Unit =
+ replyingSet("贴贴", "贴贴")
+ }
+
+ object Progynova extends ITelegramCommand {
+ override val name: String = "install"
+ override val aliases: Array[ICommandAlias]|Null = null
+ override val paramRule: String = ""
+ override val description: String = "抽取一个神秘盒子"
+ override def execute (using command: InputCommand, event: Update): Unit = {
+ coeur.account exec new SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_PROGYNOVA
+ ).replyToMessageId(event.message.messageId)
+ }
+ }
+
+ private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = {
+ val isNew = event.message.replyToMessage == null
+ val target = if (isNew) event.message else event.message.replyToMessage
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ if (isNew) whileNew else whileRec
+ ).replyToMessageId(target.messageId).parseMode(ParseMode HTML)
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala
new file mode 100644
index 0000000..0980f68
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala
@@ -0,0 +1,29 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.UseMath.over
+import cc.sukazyo.cono.morny.util.UseRandom.*
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendMessage
+
+//noinspection NonAsciiCharacters
+class 私わね (using coeur: MornyCoeur) extends ISimpleCommand {
+
+ override val name: String = "me"
+ override val aliases: Array[ICommandAlias] | Null = null
+
+ override def execute (using command: InputCommand, event: Update): Unit = {
+
+ if ((1 over 521) chance_is true) {
+ val text = "/打假"
+ coeur.account exec new SendMessage(
+ event.message.chat.id,
+ text
+ ).replyToMessageId(event.message.messageId)
+ }
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala
new file mode 100644
index 0000000..ea3149b
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala
@@ -0,0 +1,21 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListenerManager
+import cc.sukazyo.cono.morny.MornyCoeur
+
+class MornyEventListeners (using manager: EventListenerManager) (using coeur: MornyCoeur) {
+
+ manager.register(
+ // ACTIVITY_RECORDER
+ // KUOHUANHUAN_NEED_SLEEP
+ OnUniMeowTrigger(using coeur.commands),
+ OnUserRandom(),
+ OnQuestionMarkReply(),
+ OnUserSlashAction(),
+ OnCallMe(),
+ OnCallMsgSend(),
+ OnMedicationNotifyApply(),
+ OnEventHackHandle()
+ )
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala
new file mode 100644
index 0000000..38b8f95
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala
@@ -0,0 +1,39 @@
+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.bot.query.{InlineQueryUnit, MornyQueries}
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.InlineQueryResult
+import com.pengrad.telegrambot.request.AnswerInlineQuery
+
+import scala.collection.mutable.ListBuffer
+import scala.language.postfixOps
+import scala.reflect.ClassTag
+
+class MornyOnInlineQuery (using queryManager: MornyQueries) (using coeur: MornyCoeur) extends EventListener {
+
+ override def onInlineQuery (using update: Update): Boolean = {
+
+ val results: List[InlineQueryUnit[_]] = queryManager query update
+
+ var cacheTime = Int.MaxValue
+ var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL
+ val resultAnswers = ListBuffer[InlineQueryResult[_]]()
+ for (r <- results) {
+ if (cacheTime > r.cacheTime) cacheTime = r.cacheTime
+ if (r isPersonal) isPersonal = true
+ resultAnswers += r.result
+ }
+
+ if (results isEmpty) return false
+
+ coeur.account exec AnswerInlineQuery(
+ update.inlineQuery.id, resultAnswers toArray:_*
+ ).cacheTime(cacheTime).isPersonal(isPersonal)
+ true
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala
new file mode 100644
index 0000000..541070b
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala
@@ -0,0 +1,34 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.command.MornyCommands
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.{Message, Update}
+
+class MornyOnTelegramCommand (using commandManager: MornyCommands) (using coeur: MornyCoeur) extends EventListener {
+
+ override def onMessage (using update: Update): Boolean = {
+
+ def _isCommandMessage(message: Message): Boolean =
+ if message.text eq null then false
+ else if !(message.text startsWith "/") then false
+ else if message.text startsWith "/ " then false
+ else true
+
+ if !_isCommandMessage(update.message) then return false
+ val inputCommand = InputCommand(update.message.text drop 1)
+ if (!(inputCommand.command matches "^\\w+$"))
+ logger debug "not command"
+ false
+ else if ((inputCommand.target ne null) && (inputCommand.target != coeur.username))
+ logger debug "not morny command"
+ false
+ else
+ logger debug "is command"
+ commandManager.execute(using inputCommand)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala
new file mode 100644
index 0000000..89f09cd
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala
@@ -0,0 +1,17 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.MornyCoeur
+import com.pengrad.telegrambot.model.Update
+
+class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
+
+ private def isOutdated (timestamp: Int): Boolean =
+ timestamp < (coeur.config.eventOutdatedTimestamp/1000)
+
+ override def onMessage (using update: Update): Boolean = isOutdated(update.message.date)
+ override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date)
+ override def onChannelPost (using update: Update): Boolean = isOutdated(update.channelPost.date)
+ override def onEditedChannelPost (using update: Update): Boolean = isOutdated(update.editedChannelPost.date)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
new file mode 100644
index 0000000..244ee66
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
@@ -0,0 +1,104 @@
+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.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.{Chat, Message, Update, User}
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
+
+import scala.language.postfixOps
+
+class OnCallMe (using coeur: MornyCoeur) extends EventListener {
+
+ private val me = coeur.config.trustedMaster
+
+ override def onMessage (using update: Update): Boolean = {
+
+ if update.message.text == null then return false
+ if update.message.chat.`type` != (Chat.Type Private) then return false
+
+ //noinspection ScalaUnnecessaryParentheses
+ val success = if me == -1 then false else
+ (update.message.text toLowerCase) match
+ case "steam" | "sbeam" | "sdeam" =>
+ requestItem(update.message.from, "STEAM LIBRARY")
+ case "hana paresu" | "花宫" | "内群" =>
+ requestItem(update.message.from, "Hana Paresu")
+ case "dinner" | "lunch" | "breakfast" | "meal" | "eating" | "安妮今天吃什么" =>
+ requestLastDinner(update.message)
+ case cc if cc startsWith "cc::" =>
+ requestCustom(update.message)
+ case _ =>
+ return false
+
+ if success then
+ coeur.account exec SendSticker(
+ update.message.chat.id,
+ TelegramStickers ID_SENT
+ ).replyToMessageId(update.message.messageId)
+ else
+ coeur.account exec SendSticker(
+ update.message.chat.id,
+ TelegramStickers ID_501
+ ).replyToMessageId(update.message.messageId)
+
+ true
+
+ }
+
+ private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Boolean =
+ coeur.account exec SendMessage(
+ me,
+ s"""request $itemHTML
+ |from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}"""
+ .stripMargin
+ ).parseMode(ParseMode HTML)
+ true
+
+ private def requestLastDinner (req: Message): Boolean = {
+ if coeur.config.dinnerChatId == -1 then return false
+ var isAllowed = false
+ var lastDinnerData: Message|Null = null
+ if (coeur.trusted isTrusted_dinnerReader req.from.id) {
+ // todo: have issues
+ // i dont want to test it anymore... it might be deprecated soon
+ lastDinnerData = (coeur.account exec GetChat(coeur.config.dinnerChatId)).chat.pinnedMessage
+ val sendResp = coeur.account exec ForwardMessage(
+ req.from.id,
+ lastDinnerData.forwardFromChat.id,
+ lastDinnerData.forwardFromMessageId
+ )
+ import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+ def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue;
+ coeur.account exec SendMessage(
+ req.from.id,
+ "on %s [UTC+8]
\n- %s
before".formatted(
+ h(formatDate(lastDinner_dateMillis, 8)),
+ h(formatDuration(lastDinner_dateMillis))
+ )
+ ).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId)
+ isAllowed = true
+ } else {
+ coeur.account exec SendSticker(
+ req.from.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(req.messageId)
+ }
+ import Math.abs
+ requestItem(
+ req.from, "Last Annie Dinner",
+ if isAllowed then s"Allowed and returned https://t.me/c/${abs(lastDinnerData.forwardFromChat.id+1000000000000L)}/${lastDinnerData.forwardFromMessageId}"
+ else "Forbidden by perm check."
+ )
+ }
+
+ private def requestCustom (message: Message): Boolean =
+ requestItem(message.from, "[???]")
+ coeur.account exec ForwardMessage(me, message.chat.id, message.messageId)
+ true
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala
new file mode 100644
index 0000000..db920c0
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala
@@ -0,0 +1,154 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.{Chat, Message, MessageEntity, Update}
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{GetChat, SendMessage, SendSticker}
+
+import scala.collection.mutable.ArrayBuffer
+import scala.language.postfixOps
+import scala.util.matching.Regex
+
+class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
+
+ private val REGEX_MSG_SENDREQ_DATA_HEAD: Regex = "^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$"r
+
+ case class MessageToSend (
+ message: String|Null,
+ entities: Array[MessageEntity]|Null,
+ parseMode: ParseMode|Null,
+ targetId: Long
+ ) {
+ def toSendMessage (target_override: Long|Null = null): SendMessage =
+ val useTarget = if target_override == null then targetId else target_override
+ val sendMessage = SendMessage(useTarget, message)
+ if entities ne null then sendMessage.entities(entities:_*)
+ if parseMode ne null then sendMessage.parseMode(parseMode)
+ sendMessage
+ }
+ private object MessageToSend:
+ def from (raw: Message): MessageToSend = {
+ raw.text match
+ case REGEX_MSG_SENDREQ_DATA_HEAD(_target, _parseMode, _body) =>
+ val target = _target toLong
+ val parseMode: ParseMode | Null = _parseMode match
+ case "*markdown" | "*md" | "*m↓" => ParseMode MarkdownV2
+ case "*md1" => ParseMode Markdown
+ case "*html" => ParseMode HTML
+ case _ => null
+ val bodyOffset = "*msg".length + _target.length + (if _parseMode eq null then 0 else _parseMode.length) + 1
+ val entities = ArrayBuffer.empty[MessageEntity]
+ if (raw.entities ne null) for (e <- raw.entities)
+ val _parsed = MessageEntity(e.`type`, e.offset - bodyOffset, e.length)
+ if e.url ne null then _parsed.url(e.url)
+ if e.user ne null then _parsed.user(e.user)
+ if e.language ne null then _parsed.language(e.language)
+ if e.customEmojiId ne null then _parsed.language(e.language)
+ entities += _parsed
+ MessageToSend(_body, entities toArray, parseMode, target)
+ case _ => null
+ }
+
+ override def onMessage (using update: Update): Boolean = {
+
+ val message = update.message
+
+ if message.chat.`type` != Chat.Type.Private then return false
+ if message.text eq null then return false
+ if !(message.text startsWith "*msg") then return false
+
+ if (!(coeur.trusted isTrusted message.from.id))
+ coeur.account exec SendSticker(
+ message.chat.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(message.messageId)
+ return true
+
+ if (message.text == "*msgsend") {
+
+ if (message.replyToMessage eq null) return answer404
+ val messageToSend = MessageToSend from message.replyToMessage
+ if ((messageToSend eq null) || (messageToSend.message eq null)) return answer404
+ val sendResponse = coeur.account execute messageToSend.toSendMessage()
+
+ if (sendResponse isOk) {
+ coeur.account exec SendSticker(
+ update.message.chat.id,
+ TelegramStickers ID_SENT
+ ).replyToMessageId(update.message.messageId)
+ } else {
+ coeur.account exec SendMessage(
+ update.message.chat.id,
+ // language=html
+ s"""${sendResponse.errorCode} FAILED
+ |${sendResponse.description}
"""
+ .stripMargin
+ ).replyToMessageId(update.message.messageId).parseMode(ParseMode HTML)
+ }
+
+ return true
+
+ }
+
+ val messageToSend: MessageToSend =
+ val raw: Message =
+ if (message.text == "*msg")
+ if message.replyToMessage eq null then return answer404
+ else message.replyToMessage
+ else if (message.text startsWith "*msg")
+ message
+ else return answer404
+ val _toSend = MessageToSend from raw
+ if _toSend eq null then return answer404
+ else _toSend
+
+ val targetChatResponse = coeur.account execute GetChat(messageToSend.targetId)
+ if (targetChatResponse isOk) {
+ def getChatDescriptionHTML (chat: Chat): String =
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+ // language=html
+ s"""${h(chat.id toString)}@${h(chat.`type`.name)}${if (chat.`type` != Chat.Type.Private) ":::" else ""}
+ |${chat.typeTag} ${h(chat.safe_name)} ${chat.safe_linkHTML}"""
+ .stripMargin
+ coeur.account exec SendMessage(
+ update.message.chat.id,
+ getChatDescriptionHTML(targetChatResponse.chat)
+ ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
+ } else {
+ coeur.account exec SendMessage(
+ update.message.chat.id,
+ // language=html
+ s"""${targetChatResponse.errorCode} FAILED
+ |${targetChatResponse.description}
"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
+ }
+
+ if messageToSend.message eq null then return true
+ val testSendResponse = coeur.account execute
+ messageToSend.toSendMessage(update.message.chat.id).replyToMessageId(update.message.messageId)
+ if (!(testSendResponse isOk))
+ coeur.account exec SendMessage(
+ update.message.chat.id,
+ // language=html
+ s"""${testSendResponse.errorCode} FAILED
+ |${testSendResponse.description}
"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
+
+ true
+
+ }
+
+ private def answer404 (using update: Update): Boolean =
+ coeur.account exec SendSticker(
+ update.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(update.message.messageId)
+ true
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala
new file mode 100644
index 0000000..d6bc1f6
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala
@@ -0,0 +1,47 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.MornyCoeur
+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 scala.collection.mutable
+import scala.language.postfixOps
+
+class OnEventHackHandle (using coeur: MornyCoeur) extends EventListener {
+
+ import coeur.daemons.eventHack.trigger
+
+ override def onMessage (using update: Update): Boolean =
+ trigger(update.message.chat.id, update.message.from.id)
+ override def onEditedMessage (using update: Update): Boolean =
+ trigger(update.editedMessage.chat.id, update.editedMessage.from.id)
+ override def onChannelPost (using update: Update): Boolean =
+ trigger(update.channelPost.chat.id, 0)
+ override def onEditedChannelPost (using update: Update): Boolean =
+ trigger(update.editedChannelPost.chat.id, 0)
+ override def onInlineQuery (using update: Update): Boolean =
+ trigger(0, update.inlineQuery.from.id)
+ override def onChosenInlineResult (using update: Update): Boolean =
+ trigger(0, update.chosenInlineResult.from.id)
+ override def onCallbackQuery (using update: Update): Boolean =
+ trigger(0, update.callbackQuery.from.id)
+ override def onShippingQuery (using update: Update): Boolean =
+ trigger(0, update.shippingQuery.from.id)
+ override def onPreCheckoutQuery (using update: Update): Boolean =
+ trigger(0, update.preCheckoutQuery.from.id)
+ override def onPoll (using update: Update): Boolean =
+ trigger(0, 0)
+ override def onPollAnswer (using update: Update): Boolean =
+ trigger(0, update.pollAnswer.user.id)
+ override def onMyChatMemberUpdated (using update: Update): Boolean =
+ trigger(update.myChatMember.chat.id, update.myChatMember.from.id)
+ override def onChatMemberUpdated (using update: Update): Boolean =
+ trigger(update.chatMember.chat.id, update.chatMember.from.id)
+ override def onChatJoinRequest (using update: Update): Boolean =
+ trigger(update.chatJoinRequest.chat.id, update.chatJoinRequest.from.id)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala
new file mode 100644
index 0000000..25b29c1
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala
@@ -0,0 +1,21 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons}
+import com.pengrad.telegrambot.model.{Message, Update}
+
+class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
+
+ override def onEditedMessage (using event: Update): Boolean =
+ editedMessageProcess(event.editedMessage)
+ override def onEditedChannelPost (using event: Update): Boolean =
+ editedMessageProcess(event.editedChannelPost)
+
+ private def editedMessageProcess (edited: Message): Boolean = {
+ if edited.chat.id != coeur.config.medicationNotifyToChat then return false
+ coeur.daemons.medicationTimer.refreshNotificationWrite(edited)
+ true
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala
new file mode 100644
index 0000000..1aff5f2
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala
@@ -0,0 +1,44 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply.isAllMessageMark
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+import scala.util.boundary
+
+class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener {
+
+ override def onMessage (using event: Update): Boolean = {
+
+ if event.message.text eq null then return false
+
+ import cc.sukazyo.cono.morny.util.UseMath.over
+ import cc.sukazyo.cono.morny.util.UseRandom.chance_is
+ if (1 over 8) chance_is false then return false
+ if !isAllMessageMark(using event.message.text) then return false
+
+ coeur.account exec SendMessage(
+ event.message.chat.id, event.message.text
+ ).replyToMessageId(event.message.messageId)
+ true
+
+ }
+
+}
+
+object OnQuestionMarkReply {
+
+ private val QUESTION_MARKS = Set('?', '?', '¿', '⁈', '⁇', '‽', '❔', '❓')
+
+ def isAllMessageMark (using text: String): Boolean = {
+ boundary[Boolean] {
+ for (c <- text) if QUESTION_MARKS contains c then boundary.break(false)
+ true
+ }
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala
new file mode 100644
index 0000000..d1c97da
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala
@@ -0,0 +1,24 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.bot.command.MornyCommands
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.MornyCoeur
+import com.pengrad.telegrambot.model.Update
+
+class OnUniMeowTrigger (using commands: MornyCommands) (using coeur: MornyCoeur) extends EventListener {
+
+ override def onMessage (using update: Update): Boolean = {
+
+ if update.message.text eq null then return false
+ var ok = false
+ for ((name, command) <- commands.commands_uni)
+ val _name = "/"+name
+ if (_name == update.message.text)
+ command.execute(using InputCommand(_name))
+ ok = true
+ ok
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala
new file mode 100644
index 0000000..0611122
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala
@@ -0,0 +1,41 @@
+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.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+class OnUserRandom (using coeur: MornyCoeur) extends EventListener {
+
+ private val USER_OR_QUERY = "^(.+)(?:还是|or)(.+)$"r
+ private val USER_IF_QUERY = "^(.+)(?:吗\\?|?|\\?|吗?)$"r
+
+ override def onMessage(using update: Update): Boolean = {
+
+ if update.message.text == null then return false
+ if !(update.message.text startsWith "/") then return false
+
+ import cc.sukazyo.cono.morny.util.UseRandom.rand_half
+ val query = update.message.text substring 1
+ val result: String|Null = query match
+ case USER_OR_QUERY(_con1, _con2) =>
+ if rand_half then _con1 else _con2
+ case USER_IF_QUERY(_con) =>
+ // for capability with [[OnQuestionMarkReply]]
+ if OnQuestionMarkReply.isAllMessageMark(using _con) then return false
+ (if rand_half then "不" else "") + _con
+ case _ => null
+
+ if result == null then return false
+
+ coeur.account exec SendMessage(
+ update.message.chat.id, result
+ ).replyToMessageId(update.message.messageId)
+ true
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala
new file mode 100644
index 0000000..eb3e119
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala
@@ -0,0 +1,79 @@
+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.tgapi.formatting.TelegramFormatter.*
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.util.UniversalCommand
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
+
+ private val TG_FORMAT = "^\\w+(@\\w+)?$"r
+
+ override def onMessage (using update: Update): Boolean = {
+
+ val text = update.message.text
+ if text == null then return false
+
+ if (text startsWith "/") {
+
+ // there has to be some special conditions for DP7
+ // due to I have left DP7, I closed those special
+ // conditions.
+ // that is 2022, May 28th
+ // when one year goes, These code have rewrite with
+ // scala, those commented code is removed permanently.
+ // these message, here to remember the old DP7.
+
+ val actions = UniversalCommand.Lossy(text)
+ actions(0) = actions(0) substring 1
+
+ actions(0)
+
+ actions(0) match
+ // ignore Telegram command like
+ case TG_FORMAT(_) =>
+ return false
+ // ignore Path link
+ case x if x contains "/" => return false
+ case _ =>
+
+ val isHardParse = actions(0) isBlank
+ def hp_len(i: Int) = if isHardParse then i+1 else i
+ if isHardParse && actions.length < 2 then return false
+ val v_verb = actions(hp_len(0))
+ val hasObject = actions.length != hp_len(1)
+ val v_object =
+ if hasObject then
+ actions slice(hp_len(1), actions.length) mkString " "
+ else ""
+ val origin = update.message
+ val target =
+ if update.message.replyToMessage == null then
+ origin
+ else update.message.replyToMessage
+
+ coeur.account exec SendMessage(
+ update.message.chat.id,
+ "%s %s%s %s %s!".format(
+ origin.sender_firstnameRefHTML,
+ h(v_verb), if hasObject then "" else "了",
+ if (origin == target)
+ s"自己"
+ else target.sender_firstnameRefHTML,
+ if hasObject then h(v_object+" ") else ""
+ )
+ ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
+ true
+
+ } else false
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala
new file mode 100644
index 0000000..a7a2b99
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala
@@ -0,0 +1,12 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import com.pengrad.telegrambot.model.Update
+
+import javax.annotation.Nullable
+
+trait ITelegramQuery {
+
+ def query (event: Update): List[InlineQueryUnit[_]] | Null
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/InlineQueryUnit.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/InlineQueryUnit.scala
new file mode 100644
index 0000000..b15f4b6
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/InlineQueryUnit.scala
@@ -0,0 +1,27 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.bot.query.InlineQueryUnit.defaults
+import com.pengrad.telegrambot.model.request.InlineQueryResult
+
+object InlineQueryUnit {
+
+ object defaults:
+ val CACHE_TIME = 300
+ val IS_PERSONAL = false
+
+}
+
+class InlineQueryUnit[T <: InlineQueryResult[T]](val result: T) {
+
+ var cacheTime: Int = defaults.CACHE_TIME
+ var isPersonal: Boolean = defaults.IS_PERSONAL
+
+ def cacheTime (v: Int): InlineQueryUnit[T] =
+ this.cacheTime = v
+ this
+
+ def isPersonal (v: Boolean): InlineQueryUnit[T] =
+ this.isPersonal = v
+ this
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala
new file mode 100644
index 0000000..11c6a12
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala
@@ -0,0 +1,27 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.bot.query
+import cc.sukazyo.cono.morny.MornyCoeur
+import com.pengrad.telegrambot.model.Update
+
+import scala.collection.mutable.ListBuffer
+
+class MornyQueries (using MornyCoeur) {
+
+ private val queryInstances = Set[ITelegramQuery](
+ RawText(),
+ MyInformation(),
+ ShareToolTwitter(),
+ ShareToolBilibili()
+ )
+
+ def query (event: Update): List[InlineQueryUnit[_]] = {
+ val results = ListBuffer[InlineQueryUnit[_]]()
+ for (instance <- queryInstances) {
+ val r = instance query event
+ if (r != null) results ++= r
+ }
+ results.result()
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala
new file mode 100644
index 0000000..1b27068
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala
@@ -0,0 +1,30 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode}
+
+import scala.language.postfixOps
+
+class MyInformation extends ITelegramQuery {
+
+ private val ID_PREFIX = "[morny/info/me]"
+ private val TITLE = "My Account Information"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if !((event.inlineQuery.query eq null) || (event.inlineQuery.query isEmpty)) then return null
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX), TITLE,
+ new InputTextMessageContent(
+ TelegramUserInformation getFormattedInformation event.inlineQuery.from
+ ).parseMode(ParseMode HTML)
+ )).isPersonal(true).cacheTime(10)
+ )
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala
new file mode 100644
index 0000000..036fb1d
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala
@@ -0,0 +1,26 @@
+package cc.sukazyo.cono.morny.bot.query
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent}
+
+import scala.language.postfixOps
+
+class RawText extends ITelegramQuery {
+
+ private val ID_PREFIX = "[morny/r/text]"
+ private val TITLE = "Raw Text"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null || (event.inlineQuery.query isBlank)) return null
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX, event.inlineQuery.query), TITLE,
+ InputTextMessageContent(event.inlineQuery.query)
+ ))
+ )
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala
new file mode 100644
index 0000000..f73fed6
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala
@@ -0,0 +1,77 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
+import cc.sukazyo.cono.morny.util.BiliTool
+import cc.sukazyo.cono.morny.util.UseSelect.select
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode}
+
+import scala.language.postfixOps
+import scala.util.matching.Regex
+
+class ShareToolBilibili extends ITelegramQuery {
+
+ private val TITLE_BILI_AV = "[bilibili] Share video / av"
+ private val TITLE_BILI_BV = "[bilibili] Share video / BV"
+ private val ID_PREFIX_BILI_AV = "[morny/share/bili/av]"
+ private val ID_PREFIX_BILI_BV = "[morny/share/bili/bv]"
+ private val LINK_PREFIX = "https://bilibili.com/video/"
+ private val REGEX_BILI_VIDEO: Regex = "^(?:(?:https?://)?(?:www\\.)?bilibili\\.com(?:/s)?/video/((?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))/?(\\?(?:p=(\\d+))?.*)?|(?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))$"r
+ private val SHARE_FORMAT_HTML = "%s"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null) return null
+
+ event.inlineQuery.query match
+ case REGEX_BILI_VIDEO(_url_v, _url_av, _url_bv, _url_param, _url_v_part, _raw_av, _raw_bv) =>
+
+ logger debug
+ s"""====== Share Tool Bilibili Catch ok
+ |1: ${_url_v}
+ |2: ${_url_av}
+ |3: ${_url_bv}
+ |4: ${_url_param}
+ |5: ${_url_v_part}
+ |6: ${_raw_av}
+ |7: ${_raw_bv}"""
+ .stripMargin
+
+ var av = select(_url_av, _raw_av)
+ var bv = select(_url_bv, _raw_bv)
+ logger trace s"catch id av[$av] bv[$bv]"
+ val part: Int|Null = if (_url_v_part!=null) _url_v_part toInt else null
+ logger trace s"catch video part[$part]"
+
+ if (av == null) {
+ assert (bv != null)
+ av = BiliTool.toAv(bv) toString;
+ logger trace s"converted bv[$av] to av[$av]"
+ } else {
+ bv = BiliTool.toBv(av toLong)
+ logger trace s"converted av[$av] to bv[$bv]"
+ }
+
+ val id_av = s"av$av"
+ val id_bv = s"BV$bv"
+ val linkParams = if (part!=null) s"?p=$part" else ""
+ val link_av = LINK_PREFIX + id_av + linkParams
+ val link_bv = LINK_PREFIX + id_bv + linkParams
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX_BILI_AV+av), TITLE_BILI_AV+av,
+ InputTextMessageContent(SHARE_FORMAT_HTML.format(link_av, id_av)).parseMode(ParseMode HTML)
+ )),
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX_BILI_BV + bv), TITLE_BILI_BV + bv,
+ InputTextMessageContent(SHARE_FORMAT_HTML.format(link_bv, id_bv)).parseMode(ParseMode HTML)
+ ))
+ )
+
+ case _ => null
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala
new file mode 100644
index 0000000..a550501
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala
@@ -0,0 +1,40 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.InlineQueryResultArticle
+
+import scala.language.postfixOps
+import scala.util.matching.Regex
+
+class ShareToolTwitter extends ITelegramQuery {
+
+ private val TITLE_VX = "[tweet] Share as VxTwitter"
+ private val TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)"
+ private val ID_PREFIX_VX = "[morny/share/twitter/vxtwi]"
+ private val ID_PREFIX_VX_COMBINED = "[morny/share/twitter/vxtwi_combine]"
+ private val REGEX_TWEET_LINK: Regex = "^(?:https?://)?((?:(?:c\\.)?vx|fx|www\\.)?twitter\\.com)/((\\w+)/status/(\\d+)(?:/photo/(\\d+))?)/?(\\?[\\w&=-]+)?$"r
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null) return null
+
+ event.inlineQuery.query match
+
+ case REGEX_TWEET_LINK(_, _path_data, _, _, _, _) =>
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX_VX+event.inlineQuery.query), TITLE_VX,
+ s"https://vxtwitter.com/$_path_data"
+ )),
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineQueryId(ID_PREFIX_VX_COMBINED+event.inlineQuery.query), TITLE_VX_COMBINED,
+ s"https://c.vxtwitter.com/$_path_data"
+ ))
+ )
+
+ case _ => null
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala
new file mode 100644
index 0000000..ee1ab67
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala
@@ -0,0 +1,50 @@
+package cc.sukazyo.cono.morny.daemon
+
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+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 scala.collection.mutable
+
+class EventHacker (using coeur: MornyCoeur) {
+
+ private case class Hacker (from_chat: Long, from_message: Long):
+ override def toString: String = s"$from_chat/$from_message"
+
+ enum HackType:
+ case USER
+ case GROUP
+ case ANY
+
+ private val hackers = mutable.HashMap.empty[String, Hacker]
+
+ def registerHack (from_message: Long, from_user: Long, from_chat: Long, t: HackType): Unit =
+ val record = t match
+ case HackType.USER => s"(($from_user))"
+ case HackType.GROUP => s"{{$from_chat}}"
+ case HackType.ANY => "[[]]"
+ hackers += (record -> Hacker(from_chat, from_message))
+ logger debug s"add hacker track $record"
+
+ def trigger (chat: Long, fromUser: Long)(using update: Update): Boolean = {
+ logger debug s"got event signed {{$chat}}(($fromUser))"
+ val x: Hacker =
+ if hackers contains s"(($fromUser))" then (hackers remove s"(($fromUser))") get
+ else if hackers contains s"{{$chat}}" then (hackers remove s"{{$chat}}") get
+ else if hackers contains "[[]]" then (hackers remove "[[]]") get
+ else return false
+ logger debug s"hacked event by $x"
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+ coeur.account exec SendMessage(
+ x.from_chat,
+ // language=html
+ s"${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}
"
+ ).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt)
+ true
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala
new file mode 100644
index 0000000..3c61872
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala
@@ -0,0 +1,103 @@
+package cc.sukazyo.cono.morny.daemon
+
+import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.daemon.MedicationTimer.calcNextRoutineTimestamp
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.pengrad.telegrambot.model.{Message, MessageEntity}
+import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
+import com.pengrad.telegrambot.response.SendResponse
+
+import java.time.{LocalDateTime, ZoneOffset}
+import scala.collection.mutable.ArrayBuffer
+import scala.language.implicitConversions
+
+class MedicationTimer (using coeur: MornyCoeur) extends Thread {
+
+ private val NOTIFY_MESSAGE = "🍥⏲"
+ private val DAEMON_THREAD_NAME_DEF = "MedicationTimer"
+
+ private val use_timeZone = coeur.config.medicationTimerUseTimezone
+ import scala.jdk.CollectionConverters.SetHasAsScala
+ private val notify_atHour: Set[Int] = coeur.config.medicationNotifyAt.asScala.toSet.map(_.intValue)
+ private val notify_toChat = coeur.config.medicationNotifyToChat
+
+ this.setName(DAEMON_THREAD_NAME_DEF)
+
+ private var lastNotify_messageId: Option[Int] = None
+
+ override def run (): Unit = {
+
+ if ((notify_toChat == -1) || (notify_atHour isEmpty)) {
+ logger info "Medication Timer disabled : related param is not complete set"
+ return
+ }
+
+ logger info "Medication Timer started."
+ while (!this.isInterrupted) {
+ try {
+ waitToNextRoutine()
+ sendNotification()
+ } catch
+ case _: InterruptedException =>
+ interrupt()
+ logger info "MedicationTimer was interrupted, will be exit now"
+ case ill: IllegalArgumentException =>
+ logger warn "MedicationTimer will not work due to: " + ill.getMessage
+ interrupt()
+ case e =>
+ logger error
+ s"""unexpected error occurred on NotificationTimer
+ |${exceptionLog(e)}"""
+ .stripMargin
+ coeur.daemons.reporter.exception(e)
+ }
+ logger info "Medication Timer stopped."
+
+ }
+
+ private def sendNotification(): Unit = {
+ val sendResponse: SendResponse = coeur.account exec SendMessage(notify_toChat, NOTIFY_MESSAGE)
+ if sendResponse isOk then lastNotify_messageId = Some(sendResponse.message.messageId)
+ else lastNotify_messageId = None
+ }
+
+ @throws[InterruptedException | IllegalArgumentException]
+ private def waitToNextRoutine (): Unit = {
+ Thread sleep calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour)
+ }
+
+ def refreshNotificationWrite (edited: Message): Unit = {
+ if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return
+ import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
+ val editTime = formatDate(edited.editDate*1000, use_timeZone.getTotalSeconds/60/60)
+ val entities = ArrayBuffer.empty[MessageEntity]
+ if edited.entities ne null then entities ++= edited.entities
+ entities += MessageEntity(MessageEntity.Type.italic, edited.text.length + "\n-- ".length, editTime.length)
+ coeur.account exec EditMessageText(
+ notify_toChat,
+ edited.messageId,
+ edited.text + s"\n-- $editTime --"
+ ).entities(entities toArray:_*)
+ lastNotify_messageId = None
+ }
+
+}
+
+object MedicationTimer {
+
+ @throws[IllegalArgumentException]
+ private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = {
+ if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set")
+ var time = LocalDateTime.ofEpochSecond(
+ baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt,
+ zone
+ ).withMinute(0).withSecond(0).withNano(0)
+ time = time plusHours 1
+ while (!(notifyAt contains(time getHour))) {
+ time = time plusHours 1
+ }
+ (time toInstant zone) toEpochMilli
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala
new file mode 100644
index 0000000..96ae18c
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala
@@ -0,0 +1,31 @@
+package cc.sukazyo.cono.morny.daemon
+
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.MornyCoeur
+
+class MornyDaemons (using val coeur: MornyCoeur) {
+
+ val medicationTimer: MedicationTimer = MedicationTimer()
+ val reporter: MornyReport = MornyReport()
+ val eventHack: EventHacker = EventHacker()
+
+ def start (): Unit = {
+ logger info "ALL Morny Daemons starting..."
+ // TrackerDataManager.init();
+ medicationTimer.start()
+ logger info "Morny Daemons started."
+
+ }
+
+ def stop (): Unit = {
+ logger.info("stopping All Morny Daemons...")
+ // TrackerDataManager.DAEMON.interrupt();
+ medicationTimer.interrupt()
+ // TrackerDataManager.trackingLock.lock();
+ try { medicationTimer.join() }
+ catch case e: InterruptedException =>
+ e.printStackTrace(System.out)
+ logger.info("stopped ALL Morny Daemons.")
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala
new file mode 100644
index 0000000..00ff369
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala
@@ -0,0 +1,123 @@
+package cc.sukazyo.cono.morny.daemon
+
+import cc.sukazyo.cono.morny.{MornyCoeur, MornyConfig}
+import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
+import cc.sukazyo.cono.morny.data.MornyInformation.getVersionAllFullTagHTML
+import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
+import com.google.gson.GsonBuilder
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.model.User
+import com.pengrad.telegrambot.request.{BaseRequest, SendMessage}
+import com.pengrad.telegrambot.response.BaseResponse
+
+class MornyReport (using coeur: MornyCoeur) {
+
+ private val enabled = coeur.config.reportToChat != -1
+ if !enabled then
+ logger info "Morny Report is disabled : report chat is set to -1"
+
+ private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = {
+ if !enabled then return;
+ try {
+ coeur.account exec report
+ } catch case e: EventRuntimeException.ActionFailed => {
+ logger warn
+ s"""cannot execute report to telegram:
+ |${exceptionLog(e) indent 4}
+ | tg-api response:
+ |${(e.response toString) indent 4}"""
+ .stripMargin
+ }
+ }
+
+ def exception (e: Throwable, description: String|Null = null): Unit = {
+ def _tgErrFormat: String = e match
+ case api: EventRuntimeException.ActionFailed =>
+ // language=html
+ "\n\ntg-api error:\n
"
+ .formatted(GsonBuilder().setPrettyPrinting().create.toJson(api.response))
+ case _ => ""
+ executeReport(SendMessage(
+ coeur.config.reportToChat,
+ // language=html
+ s"""▌Coeur Unexpected Exception
+ |${if description ne null then h(description)+"\n" else ""}
+ |%s
$_tgErrFormat"""
+ .stripMargin
+ ).parseMode(ParseMode HTML))
+ }
+
+ def unauthenticatedAction (action: String, user: User): Unit = {
+ executeReport(SendMessage(
+ coeur.config.reportToChat,
+ // language=html
+ s"""▌User unauthenticated action
+ |action: ${h(action)}
+ |by user ${user.fullnameRefHTML}"""
+ .stripMargin
+ ).parseMode(ParseMode HTML))
+ }
+
+ def reportCoeurMornyLogin(): Unit = {
+ executeReport(SendMessage(
+ coeur.config.reportToChat,
+ // language=html
+ s"""▌Morny Logged in
+ |-v $getVersionAllFullTagHTML
+ |as user ${coeur.username}
+ |
+ |as config fields:
+ |${sectionConfigFields(coeur.config)}"""
+ .stripMargin
+ ).parseMode(ParseMode HTML))
+ }
+
+ private def sectionConfigFields (config: MornyConfig): String = {
+ val echo = StringBuilder()
+ for (field <- config.getClass.getFields) {
+ // language=html
+ echo ++= s"- ${field.getName} "
+ try {
+ if (field.isAnnotationPresent(classOf[MornyConfig.Sensitive])) {
+ echo ++= /*language=html*/ ": sensitive_field"
+ } else {
+ val value = field.get(config)
+ // language=html
+ echo ++= "= " ++= (if value eq null then "null" else s"${h(exceptionLog(e))}
${h(value.toString)}
")
+ }
+
+ } catch
+ // noinspection ScalaUnnecessaryParentheses
+ case e: (IllegalAccessException|IllegalArgumentException|NullPointerException) =>
+ // language=html
+ echo ++= s": ${h("${h(a.toString)}
"
+ case None => "UNKNOWN reason"
+ executeReport(SendMessage(
+ coeur.config.reportToChat,
+ // language=html
+ s"""▌Morny Exited
+ |from user @${coeur.username}
+ |
+ |by: $_causedTag"""
+ .stripMargin
+ ).parseMode(ParseMode HTML))
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala
new file mode 100644
index 0000000..19e4642
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala
@@ -0,0 +1,45 @@
+package cc.sukazyo.cono.morny.data
+
+import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornySystem}
+
+import java.net.InetAddress
+import java.rmi.UnknownHostException
+
+object MornyInformation {
+
+ //noinspection ScalaWeakerAccess
+ def getVersionGitTagHTML: String = {
+ if (!MornySystem.isGitBuild) return ""
+ val g = StringBuilder()
+ val cm = BuildConfig.COMMIT substring(0, 8)
+ val cp = MornySystem.currentCodePath
+ if (cp == null) g ++= s"$cm
"
+ else g ++= s"$cm"
+ if (!MornySystem.isCleanBuild) g ++= ".δ
"
+ g toString
+ }
+
+ def getVersionAllFullTagHTML: String = {
+ val v = StringBuilder()
+ v ++= s"${MornySystem VERSION_BASE}
"
+ if (MornySystem isUseDelta) v ++= s"-δ${MornySystem VERSION_DELTA}
"
+ if (MornySystem isGitBuild) v ++= "+git." ++= getVersionGitTagHTML
+ v ++= s"*${MornySystem.CODENAME toUpperCase}
"
+ v toString
+ }
+
+ //noinspection ScalaWeakerAccess
+ def getRuntimeHostname: Option[String] = {
+ try Some(InetAddress.getLocalHost.getHostName)
+ catch case _: UnknownHostException => None
+ }
+
+ def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get
+
+ def getMornyAboutLinksHTML: String =
+ s"""source code | backup
+ |反馈 / issue tracker
+ |使用说明书 / user guide & docs"""
+ .stripMargin
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyJrrp.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyJrrp.scala
new file mode 100644
index 0000000..b707576
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyJrrp.scala
@@ -0,0 +1,17 @@
+package cc.sukazyo.cono.morny.data
+
+import com.pengrad.telegrambot.model.User
+
+import scala.language.postfixOps
+
+object MornyJrrp {
+
+ def jrrp_of_telegramUser (user: User, timestamp: Long): Double =
+ jrrp_v_xmomi(user.id, timestamp/(1000*60*60*24)) * 100.0
+
+ private def jrrp_v_xmomi (identifier: Long, dayStamp: Long): Double =
+ import cc.sukazyo.cono.morny.util.CommonEncrypt.MD5
+ import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex
+ java.lang.Long.parseLong(MD5(s"$identifier@$dayStamp").toHex.substring(0, 4), 16) / (0xffff toDouble)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/NbnhhshQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/data/NbnhhshQuery.scala
new file mode 100644
index 0000000..9c2049a
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/NbnhhshQuery.scala
@@ -0,0 +1,37 @@
+package cc.sukazyo.cono.morny.data
+
+import cc.sukazyo.cono.morny.util.OkHttpPublic.MediaTypes
+import com.google.gson.Gson
+import okhttp3.{OkHttpClient, Request, RequestBody, ResponseBody}
+
+import java.io.IOException
+import scala.util.Using
+
+object NbnhhshQuery {
+
+ case class Word (name: String, trans: Array[String], inputting: Array[String])
+ case class GuessResult (words: Array[Word])
+
+ private case class GuessRequest (text: String)
+
+ private val API_URL = "https://lab.magiconch.com/api/nbnhhsh/"
+ private val API_GUESS_METHOD = "guess/"
+
+ private val httpClient = OkHttpClient()
+
+ @throws[IOException]
+ def sendGuess (text: String): GuessResult = {
+ val requestJsonText = Gson().toJson(GuessRequest(text))
+ val request = Request.Builder()
+ .url(API_URL + API_GUESS_METHOD)
+ .post(RequestBody.create(requestJsonText, MediaTypes.JSON))
+ .build
+ Using (httpClient.newCall(request).execute) { response =>
+ val body = response.body
+ if body eq null then throw IOException("Nbnhhsh Request: body is null.")
+ val x = s"{ 'words': ${body.string} }"
+ Gson().fromJson(x, classOf[GuessResult])
+ }.get
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala
new file mode 100644
index 0000000..ac45039
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala
@@ -0,0 +1,37 @@
+package cc.sukazyo.cono.morny.data
+
+import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
+import cc.sukazyo.cono.morny.MornyAssets
+import cc.sukazyo.cono.morny.daemon.MornyReport
+import cc.sukazyo.cono.morny.MornyAssets.AssetsException
+
+import java.io.IOException
+import scala.language.postfixOps
+import scala.util.Using
+
+object TelegramImages {
+
+ class AssetsFileImage (assetsPath: String) {
+
+ private var cache: Option[Array[Byte]] = None
+
+ @throws[AssetsException]
+ def get:Array[Byte] =
+ if cache isEmpty then read()
+ cache.get
+
+ @throws[AssetsException]
+ private def read (): Unit = {
+ Using ((MornyAssets.pack getResource assetsPath)read) { stream =>
+ try { this.cache = Some(stream.readAllBytes()) }
+ catch case e: IOException => {
+ throw AssetsException(e)
+ }
+ }
+ }
+
+ }
+
+ val IMG_ABOUT: AssetsFileImage = AssetsFileImage("images/featured-image@0.5x.jpg")
+
+}
diff --git a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramStickers.java
similarity index 53%
rename from src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java
rename to src/main/scala/cc/sukazyo/cono/morny/data/TelegramStickers.java
index fb3f01c..51aa33f 100644
--- a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramStickers.java
@@ -1,11 +1,9 @@
package cc.sukazyo.cono.morny.data;
-import cc.sukazyo.cono.morny.util.tgapi.ExtraAction;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-import com.pengrad.telegrambot.response.SendResponse;
-
+import javax.annotation.Nonnull;
import java.lang.reflect.Field;
+import java.util.LinkedHashMap;
+import java.util.Map;
/**
* 存放 bot 使用到的贴纸
@@ -23,42 +21,30 @@ public class TelegramStickers {
public static final String ID_SAVED = "CAACAgEAAx0CSQh32gABBExuYdB_G0srfhQldRWkBYxWzCOv4-IAApooAAJ4_MYFcjuNZszfQcQjBA";
public static final String ID_PROGYNOVA = "CAACAgUAAxkBAAICm2KEuL7UQqNP7vSPCg2DHJIND6UsAAKLAwACH4WSBszIo722aQ3jJAQ";
public static final String ID_NETWORK_ERR = "CAACAgEAAxkBAAID0WNJgNEkD726KW4vZeFlw0FlVVyNAAIXJgACePzGBb50o7O1RbxoKgQ";
+ public static final String ID_501 = "CAACAgEAAxkBAAIHbGUhJ8zm2Sb_c0YU-DYQ6xb-ZDtaAAKdJwACePzGBTOftDZL6X7vMAQ";
- public static void echoAllStickers (ExtraAction actionObject, long sentChat, int replyToMessageId) {
-
+ @Nonnull
+ public static Map${user.id}
"""
+ .stripMargin
+ userInfo ++= {
+ if (user.username eq null) // language=html
+ s"""
+ |username : null
+ |datacenter : not supported"""
+ .stripMargin
+ else // language=html
+ s"""
+ |username :
+ |- ${h(user.username)}
+ |datacenter :
+ |- ${h(getDataCenterFromUser(user.username))}
"""
+ .stripMargin
+ }
+ userInfo ++= // language=html
+ s"""
+ |display name :
+ |- ${h(user.firstName)}
${if user.lastName ne null then s"\n- ${h(user.lastName)}
" else ""}"""
+ .stripMargin
+ if (user.languageCode ne null) userInfo ++= // language=html
+ s"""
+ |language-code :
+ |- ${user.languageCode}
"""
+ .stripMargin
+
+ userInfo toString
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/package.scala b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/package.scala
new file mode 100644
index 0000000..d646076
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/package.scala
@@ -0,0 +1,4 @@
+package cc.sukazyo.cono.morny.util
+
+/** @todo docs */
+package object tgapi {}
diff --git a/src/test/java/cc/sukazyo/cono/morny/MornyCLI.java b/src/test/java/cc/sukazyo/cono/morny/MornyCLI.java
deleted file mode 100644
index 1b6eea7..0000000
--- a/src/test/java/cc/sukazyo/cono/morny/MornyCLI.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cc.sukazyo.cono.morny;
-
-import cc.sukazyo.cono.morny.util.UniversalCommand;
-
-import java.util.*;
-
-public class MornyCLI {
-
- public static void main (String[] args) {
-
- Scanner line = new Scanner(System.in);
- System.out.print("$ java -jar morny-coeur-"+GradleProjectConfigures.VERSION+".jar " );
- String x = line.nextLine();
- ServerMain.main(UniversalCommand.format(x));
-
- }
-
-}
diff --git a/src/test/java/cc/sukazyo/cono/morny/util/TestBiliTool.java b/src/test/java/cc/sukazyo/cono/morny/util/TestBiliTool.java
deleted file mode 100644
index 125d9c6..0000000
--- a/src/test/java/cc/sukazyo/cono/morny/util/TestBiliTool.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.CsvSource;
-
-import static cc.sukazyo.cono.morny.util.BiliTool.*;
-
-public class TestBiliTool {
-
- private static final String AV_BV_DATA_CSV = """
- 17x411w7KC, 170001
- 1Q541167Qg, 455017605
- 1mK4y1C7Bz, 882584971
- 1T24y197V2, 688730800
- """;
-
- @ParameterizedTest
- @CsvSource(textBlock = AV_BV_DATA_CSV)
- void testAvToBv (String bv, int av) {
- Assertions.assertEquals(bv, toBv(av));
- }
-
- @ParameterizedTest
- @CsvSource(textBlock = AV_BV_DATA_CSV)
- void testBvToAv (String bv, int av) {
- Assertions.assertEquals(av, toAv(bv));
- }
-
-}
diff --git a/src/test/java/cc/sukazyo/cono/morny/util/TestCommonConvert.java b/src/test/java/cc/sukazyo/cono/morny/util/TestCommonConvert.java
deleted file mode 100644
index 8fd9ed7..0000000
--- a/src/test/java/cc/sukazyo/cono/morny/util/TestCommonConvert.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cc.sukazyo.cono.morny.util;
-
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.CsvSource;
-import org.junit.jupiter.params.provider.MethodSource;
-
-import java.util.stream.Stream;
-
-import static cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex;
-import static cc.sukazyo.cono.morny.util.CommonConvert.byteToHex;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.params.provider.Arguments.arguments;
-
-public class TestCommonConvert {
-
- @ParameterizedTest
- @CsvSource(textBlock = """
- 0x00, 00
- 0x01, 01
- 0x20, 20
- 0x77, 77
- -0x60, a0
- 0x0a, 0a
- -0x01, ff
- -0x05, fb
- """
- )
- void testByteToHex(byte source, String expected) {
- assertEquals(expected, byteToHex(source));
- }
-
- public static Stream