diff --git a/Lagrange.Core/.editorconfig b/Lagrange.Core/.editorconfig new file mode 100644 index 0000000..2088260 --- /dev/null +++ b/Lagrange.Core/.editorconfig @@ -0,0 +1,243 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = lf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = false +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +# lwx like var +csharp_style_var_elsewhere = true +# but don't like built in types use var +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = true + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +[*.{cs,vb}] +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = lf \ No newline at end of file diff --git a/Lagrange.Core/.github/ISSUE_TEMPLATE/bug-report.yml b/Lagrange.Core/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..ed4d3ad --- /dev/null +++ b/Lagrange.Core/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,149 @@ +name: 错误报告 +description: 在使用 Lagrange 的过程中遇到了错误 +title: '[Bug?]: ' +labels: [ "bug?" ] + +body: + # User's README and agreement + - type: markdown + attributes: + value: | + ## 感谢您愿意填写错误回报! + ## 以下是一些注意事项,请务必阅读让我们能够更容易处理 + + ### ❗| 确定没有相同问题的ISSUE已被提出. + ### 🌎| 请准确填写环境信息 + ### ❔| 修改配置文件中log参数为trace或debug,并提供出现问题前后的完整日志内容。**请自行删除日志内存在的个人信息及敏感内容。** + ### ⚠ | 如果您有能力,请使用VS提供更加详细的信息. + + ## 如果您不知道如何有效、精准地表述,我们建议您先阅读[《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) + --- + - type: checkboxes + id: terms + attributes: + label: 请确保您已阅读以上注意事项,并勾选下方的确认框。 + options: + - label: "我已经仔细阅读上述内容" + required: true + - label: "我已经使用 [最新构建](https://github.com/LagrangeDev/Lagrange.Core/actions/) 测试过,问题依旧存在。" + required: true + - label: "我已经在 [Issue Tracker](https://github.com/LagrangeDev/Lagrange.Core/issues) 中找过我要提出的问题,没有找到相同问题的ISSUE。" + required: true + - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题。若这个 Issue 是关于其他非程序本身问题,则我的 Issue 可能会被无条件自动关闭或/并锁定。(其它疑问请考虑加入TG群询问或在discussions中提问) + required: true + + # User's data + - type: markdown + attributes: + value: | + ## 环境信息 + 请根据实际使用环境修改以下信息。 + + # Env | Lagrange Project + - type: dropdown + id: lagrange-project + attributes: + label: Lagrange项目 + description: 请选择具体使用/依赖的Lagrange项目 + options: + - Audio + - Core + - OneBot + - 其它(请在下方说明) + validations: + required: true + + # Env | Lagrange Commit + - type: input + id: lagrange-commit + attributes: + label: 所使用/依赖的Lagrange项目对应的commit + validations: + required: true + + # Env | VM Version + - type: dropdown + id: env-vm-ver + attributes: + label: 运行环境 + description: 选择运行 Lagrange 的系统版本 + options: + - Windows + - MacOS + - Linux + - 其它(请在下方说明) + validations: + required: true + + # Env | VM Arch + - type: dropdown + id: env-vm-arch + attributes: + label: 运行架构 + description: 请选择运行 Lagrange 的系统架构 + options: + - x64 + - x86 + - arm + - arm64 + - 其它 + validations: + required: true + + # Env | Connection type + - type: dropdown + id: env-conn-type + attributes: + label: 连接方式 + description: 如果有必要,请选择对接机器人的连接方式(例如使用Lagrange.OneBot) + options: + - HTTP + - 正向 WebSocket + - 反向 WebSocket + + # Input | Reproduce + - type: textarea + id: reproduce-steps + attributes: + label: 重现步骤 + description: | + 我们需要执行哪些操作才能让 bug 出现? + 简洁清晰的重现步骤能够帮助我们更迅速地定位问题所在。 + validations: + required: true + + # Input | Expected result + - type: textarea + id: expected + attributes: + label: 期望的结果是什么? + validations: + required: true + + # Input | Actual result + - type: textarea + id: actual + attributes: + label: 实际的结果是什么? + validations: + required: true + + # Optional | Reproduce code + - type: textarea + id: reproduce-code + attributes: + label: 简单的复现代码/链接(可选) + render: C# + + # Optional | Logging + - type: textarea + id: logging + attributes: + label: Trace 级别日志记录(可选) + render: Shell + + # Optional | Extra description + - type: textarea + id: extra-desc + attributes: + label: 补充说明(可选) diff --git a/Lagrange.Core/.github/ISSUE_TEMPLATE/feature-request.yml b/Lagrange.Core/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..d2d92de --- /dev/null +++ b/Lagrange.Core/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,37 @@ +name: 新功能提议 +description: 希望拥有新的功能 +title: '[Feature Request]: ' +labels: ["enhancement"] + +body: + # User's README + - type: markdown + attributes: + value: | + ### 为了项目的长久稳定,请不要要求提供一些敏感操作的新功能 + ### 项目人手有限,如果您有能力,请考虑提交PR + ### 当 Owner 或 Member 认为该 Feature Request 不合适,可能直接关闭 ISSUE + + # Env | Lagrange Project + - type: dropdown + id: lagrange-project + attributes: + label: Lagrange项目 + description: 请选择您希望在哪个具体 Lagrange 项目中添加新需求 + options: + - Audio + - Core + - OneBot + - 其它(请在下方说明) + validations: + required: true + + # Input | Feature Content + - type: textarea + id: feature-content + attributes: + label: 新需求内容 + description: | + 请尽可能详细描述您的需求,如果可以,请提供代码样例或参考项目 + validations: + required: true diff --git a/Lagrange.Core/.github/workflows/Lagrange.Core-nuget-push.yml b/Lagrange.Core/.github/workflows/Lagrange.Core-nuget-push.yml new file mode 100644 index 0000000..c21fa57 --- /dev/null +++ b/Lagrange.Core/.github/workflows/Lagrange.Core-nuget-push.yml @@ -0,0 +1,39 @@ +name: Lagrange.Core NuGet Push + +on: + push: + branches: + - master + paths: + - "Lagrange.Core/**" + - "Lagrange.Core.sln" + - "LICENSE" + - "README.md" + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Pack + run: | + dotnet build -c Release Lagrange.Core + dotnet pack -c Release Lagrange.Core + dotnet pack -c Release -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg Lagrange.Core + + - name: Add private GitHub registry to NuGet + run: dotnet nuget add source --username Linwenxuan05 --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/LagrangeDev/index.json" + + - name: Push generated package to GitHub registry + run: dotnet nuget push ./Lagrange.Core/bin/Release/*.nupkg --source "github" --skip-duplicate --api-key ${{ secrets.GIT_TOKEN }} + + - name: Push generated package to NuGet + run: dotnet nuget push ./Lagrange.Core/bin/Release/*.nupkg --source https://api.nuget.org/v3/index.json --skip-duplicate --api-key ${{ secrets.NUGETAPIKEY }} diff --git a/Lagrange.Core/.github/workflows/Lagrange.OneBot-build.yml b/Lagrange.Core/.github/workflows/Lagrange.OneBot-build.yml new file mode 100644 index 0000000..8d73aa5 --- /dev/null +++ b/Lagrange.Core/.github/workflows/Lagrange.OneBot-build.yml @@ -0,0 +1,72 @@ +name: Lagrange.OneBot Build + +on: + push: + branches: + - master + paths: + - "Lagrange.Core/**" + - "Lagrange.OneBot/**" + - "Lagrange.Core.sln" + - "LICENSE" + - "!Lagrange.OneBot/Resources/Dockerfile" + - "!Lagrange.OneBot/Resources/docker-entrypoint.sh" + pull_request: + branches: + - master + paths: + - "Lagrange.Core/**" + - "Lagrange.OneBot/**" + - "Lagrange.Core.sln" + - "LICENSE" + - "!Lagrange.OneBot/Resources/Dockerfile" + - "!Lagrange.OneBot/Resources/docker-entrypoint.sh" + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + runtimeIdentifier: + [ + win-x64, + win-x86, + linux-x64, + linux-arm, + linux-arm64, + osx-x64, + osx-arm64, + linux-musl-x64, + linux-musl-arm, + linux-musl-arm64, + ] + + steps: + - uses: actions/checkout@v4 + + - name: Install .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Build Lagrange.OneBot .NET 8.0 + run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --no-self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net8.0 + + - name: Build Lagrange.OneBot .NET 9.0 + run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --no-self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net9.0 + + - name: Upload binary files(${{ matrix.runtimeIdentifier }}) for .NET 8.0 + uses: actions/upload-artifact@v4 + if: github.event_name != 'pull_request' + with: + name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net8.0_NoSelfContained + path: Lagrange.OneBot/bin/Release/net8.0/${{ matrix.runtimeIdentifier }}/publish + + - name: Upload binary files(${{ matrix.runtimeIdentifier }}) for .NET 9.0 + uses: actions/upload-artifact@v4 + if: github.event_name != 'pull_request' + with: + name: Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_NoSelfContained + path: Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish diff --git a/Lagrange.Core/.github/workflows/Lagrange.OneBot-docker-push.yml b/Lagrange.Core/.github/workflows/Lagrange.OneBot-docker-push.yml new file mode 100644 index 0000000..215b1c4 --- /dev/null +++ b/Lagrange.Core/.github/workflows/Lagrange.OneBot-docker-push.yml @@ -0,0 +1,62 @@ +name: Lagrange.OneBot Docker Push + +on: + push: + branches: + - master + paths: + - "Lagrange.Core/**" + - "Lagrange.OneBot/**" + - "Lagrange.Core.sln" + - "LICENSE" + pull_request: + branches: + - master + paths: + - "Lagrange.Core/**" + - "Lagrange.OneBot/**" + - "Lagrange.Core.sln" + - "LICENSE" + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Registry + uses: docker/login-action@v3 + if: github.event_name != 'pull_request' + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GIT_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ github.repository_owner }}/lagrange.onebot + tags: | + type=edge + type=sha,event=branch + type=ref,event=tag + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: "Lagrange.OneBot/Resources/Dockerfile" + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64, linux/arm64, linux/arm diff --git a/Lagrange.Core/.github/workflows/Lagrange.OneBot-release.yml b/Lagrange.Core/.github/workflows/Lagrange.OneBot-release.yml new file mode 100644 index 0000000..c7fb0a5 --- /dev/null +++ b/Lagrange.Core/.github/workflows/Lagrange.OneBot-release.yml @@ -0,0 +1,76 @@ +name: Lagrange.OneBot Release + +on: + workflow_dispatch: + +env: + GH_TOKEN: ${{ github.token }} + +jobs: + delete-tag: + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - run: gh release delete nightly --cleanup-tag -y -R ${{ github.repository }} + + create-tag: + needs: + - delete-tag + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - run: echo -e "> ⚠️This is a nightly release.\r\n> ⚠️This is not the latest version." > note + + - run: gh release create nightly -F ./note -p -t "Nightly Release" -R ${{ github.repository }} + + build-and-upload-release: + if: ${{ always() }} + + needs: + - create-tag + + runs-on: ubuntu-latest + + permissions: + contents: write + + strategy: + matrix: + runtimeIdentifier: + [ + win-x64, + win-x86, + linux-x64, + linux-arm, + linux-arm64, + osx-x64, + osx-arm64, + linux-musl-x64, + linux-musl-arm, + linux-musl-arm64, + ] + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - run: dotnet publish Lagrange.OneBot/Lagrange.OneBot.csproj --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none -p:RuntimeIdentifier=${{ matrix.runtimeIdentifier }} --framework net9.0 + + - run: | + if [[ ${{ matrix.runtimeIdentifier }} == 'win-x64' || ${{ matrix.runtimeIdentifier }} == 'win-x86' ]]; then + zip -r Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.zip ./Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish + gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.zip -R ${{ github.repository }} + else + tar -czvf Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.tar.gz ./Lagrange.OneBot/bin/Release/net9.0/${{ matrix.runtimeIdentifier }}/publish + gh release upload nightly Lagrange.OneBot_${{ matrix.runtimeIdentifier }}_net9.0_SelfContained.tar.gz -R ${{ github.repository }} + fi diff --git a/Lagrange.Core/.gitignore b/Lagrange.Core/.gitignore new file mode 100644 index 0000000..9a7ed67 --- /dev/null +++ b/Lagrange.Core/.gitignore @@ -0,0 +1,271 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# backups +local-backup/ + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Nuget packages directory +packages/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# vscode files +.vscode/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml +*.DotSettings.user +riderModule.iml +/_ReSharper.Caches/ + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +#macos +.DS_Store + +#Lagrange.Core +Lagrange.Core/Utility/Crypto/Provider/Dandelion/*.cs +docs/node_modules +docs/.vitepress/dist +docs/.vitepress/cache +docs/api diff --git a/Lagrange.Core/Docker.md b/Lagrange.Core/Docker.md new file mode 100644 index 0000000..2b2f6a0 --- /dev/null +++ b/Lagrange.Core/Docker.md @@ -0,0 +1,51 @@ +
+ +# Lagrange.Core - Docker guide + +An Implementation of NTQQ Protocol, with Pure C#, Derived from Konata.Core + +[![Core](https://img.shields.io/badge/Lagrange-Core-blue)](#) +[![OneBot](https://img.shields.io/badge/Lagrange-OneBot-blue)](#) +[![C#](https://img.shields.io/badge/Core-%20.NET_6-blue)](#) +[![C#](https://img.shields.io/badge/OneBot-%20.NET_7-blue)](#) + +[![License](https://img.shields.io/static/v1?label=LICENSE&message=GPL-3.0&color=lightrey)](#) +[![Telegram](https://img.shields.io/endpoint?url=https%3A%2F%2Ftelegram-badge-4mbpu8e0fit4.runkit.sh%2F%3Furl%3Dhttps%3A%2F%2Ft.me%2F%2B6HNTeJO0JqtlNmRl)](https://t.me/+6HNTeJO0JqtlNmRl) + +**> English <** | [简体中文](Docker_zh.md) + +
+ +## Getting Started + +```bash +# 8081 port for ForwardWebSocket and Http-Post +# /path-to-data is used to store the files needed for the runtime +# UID Env and GID Env are used to set file permissions +docker run -td -p 8081:8081 -v /path-to-data:/app/data -e UID=$UID -e GID=$(id -g) ghcr.io/lagrangedev/lagrange.onebot:edge +``` + +> [!IMPORTANT] +> +> 1. During the first run, you may be prompted with Please Edit the appsettings.json to set configs and press any key to continue. Please choose one of the following solutions to proceed: +> +> 1. 1. Modify `/path-to-data/appsettings.json` +> 2. Restart the container using `docker restart` +> +> 2. 1. Ensure that the `-t` option is used when executing `docker run` +> 2. Modify `/path-to-data/appsettings.json` +> 3. Enter the container using `docker attach` +> 4. Press any key +> 5. Exit the container using `Ctrl + P` `Ctrl + Q` +> +> 2. If the host needs to access the Implementation (e.g., `Http`, `ForwardWebSocket`), please configure the Host of Implementation as `*` +> 3. If the implementation needs to access the host network (e.g., `HttpPost`, `ReverseWebSocket`), please configure the `Host` of implementation to be `host.docker.internal`. + +## Migration from older versions + +Move `appsettings.json`, `device.json`, `keystore.json`, `lagrange-*.db` to the same folder where you want to put them. +For example `/path-to-data` + +Delete the `ConfigPath` configuration entry in `/path-to-data/appsettings.json` + +Start the container according to [Getting Started](#getting-started) diff --git a/Lagrange.Core/Docker_zh.md b/Lagrange.Core/Docker_zh.md new file mode 100644 index 0000000..269eb28 --- /dev/null +++ b/Lagrange.Core/Docker_zh.md @@ -0,0 +1,51 @@ +
+ +# Lagrange.Core - Docker 使用指南 + +一个基于纯 C# 的 NTQQ 协议实现,源自 Konata.Core + +[![Core](https://img.shields.io/badge/Lagrange-Core-blue)](#) +[![OneBot](https://img.shields.io/badge/Lagrange-OneBot-blue)](#) +[![C#](https://img.shields.io/badge/Core-%20.NET_6-blue)](#) +[![C#](https://img.shields.io/badge/OneBot-%20.NET_7-blue)](#) + +[![License](https://img.shields.io/static/v1?label=LICENSE&message=GPL-3.0&color=lightrey)](#) +[![Telegram](https://img.shields.io/endpoint?url=https%3A%2F%2Ftelegram-badge-4mbpu8e0fit4.runkit.sh%2F%3Furl%3Dhttps%3A%2F%2Ft.me%2F%2B6HNTeJO0JqtlNmRl)](https://t.me/+6HNTeJO0JqtlNmRl) + +[English](Docker.md) | **> 简体中文 <** + +
+ +## 开始使用 + +```bash +# 8081 端口用于正向 WebSocket 和 Http-post +# /path-to-data 被用于存储程序运行时产生的文件 +# UID Env 和 GID Env 用于设置文件权限 +docker run -td -p 8081:8081 -v /path-to-data:/app/data -e UID=$UID -e GID=$(id -g) ghcr.io/lagrangedev/lagrange.onebot:edge +``` + +> [!IMPORTANT] +> +> - 首次运行时可能会提示 `Please Edit the appsettings.json to set configs and press any key to continue`,请选择以下一种方案执行: +> +> - 1. 修改 `/path-to-data/appsettings.json` +> 2. 使用 `docker restart` 重新启动容器 +> +> - 1. 确保在执行 `docker run` 时使用了 `-t` 选项 +> 2. 修改 `/path-to-data/appsettings.json` +> 3. 使用 `docker attach` 进入容器 +> 4. 按任意键 +> 5. 使用 `Ctrl + P` `Ctrl + Q` 退出容器 +> +> - 如果需要宿主需要访问实现(例如:`Http`,`ForwardWebSocket`),请将实现的 `Host` 配置为 `*` +> - 如果实现需要访问宿主网络(例如:`HttpPost`,`ReverseWebSocket`),请将实现的 `Host` 配置为 `host.docker.internal` + +## 从旧版本迁移 + +将 `appsettings.json`、`device.json`、`keystore.json`、`lagrange-*.db` 移动到您想要放置它们的相同文件夹中。 +例如 `/path-to-data` + +删除 `/path-to-data/appsettings.json` 中的 `ConfigPath` 配置 + +按照 [# 开始使用](#开始使用) 启动容器 diff --git a/Lagrange.Core/LICENSE b/Lagrange.Core/LICENSE new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/Lagrange.Core/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Lagrange.Core.Test.csproj b/Lagrange.Core/Lagrange.Core.Test/Lagrange.Core.Test.csproj new file mode 100644 index 0000000..29a01ac --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Lagrange.Core.Test.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/Lagrange.Core/Lagrange.Core.Test/Program.cs b/Lagrange.Core/Lagrange.Core.Test/Program.cs new file mode 100644 index 0000000..54d0404 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Program.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Test.Tests; + +namespace Lagrange.Core.Test; + +internal static class Program +{ + public static async Task Main(string[] args) + { + // BenchmarkRunner.Run(new DebugBuildConfig()); + await new NTLoginTest().LoginByPassword(); + // await new WtLoginTest().FetchQrCode(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Protobuf.cs b/Lagrange.Core/Lagrange.Core.Test/Protobuf.cs new file mode 100644 index 0000000..5862413 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Protobuf.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Internal.Packets.Login.Ecdh; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Test; + +public class Protobuf +{ + public void Test() + { + + var test = new SsoKeyExchange() + { + PubKey = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + GcmCalc2 = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + GcmCalc1 = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + Timestamp = 23456789, + Type = 1 + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, test); + Console.WriteLine(stream.ToArray().Hex(false, true)); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Tests/BinaryTest.cs b/Lagrange.Core/Lagrange.Core.Test/Tests/BinaryTest.cs new file mode 100644 index 0000000..4ea0f89 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Tests/BinaryTest.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Test.Tests; + +public class BinaryTest +{ + public class Binary + { + [BinaryProperty] public uint Value { get; set; } = 114514; + + [BinaryProperty] public ulong Value2 { get; set; } = 1919810; + } + + public void Test() + { + var binary = new Binary(); + var bytes = BinarySerializer.Serialize(binary); + Console.WriteLine(bytes.ToArray().Hex()); + Console.WriteLine(bytes.Length); + + var binary2 = new BinaryPacket(); + binary2.WriteUint(114514); + binary2.WriteUlong(1919810); + Console.WriteLine(binary2.ToArray().Hex()); + + var newPacket = new BinaryPacket(bytes.ToArray()); + var binary3 = newPacket.Deserialize(); + Console.WriteLine(binary3.Value); + Console.WriteLine(binary3.Value2); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Tests/EcdhTest.cs b/Lagrange.Core/Lagrange.Core.Test/Tests/EcdhTest.cs new file mode 100644 index 0000000..29878da --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Tests/EcdhTest.cs @@ -0,0 +1,82 @@ +using Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +namespace Lagrange.Core.Test.Tests; + +public class EcdhTest +{ + public static void Test() + { + { + var uncompressedPublicKey = new byte[] + { + 0x04, + 0xEB, 0xCA, 0x94, 0xD7, 0x33, 0xE3, 0x99, 0xB2, + 0xDB, 0x96, 0xEA, 0xCD, 0xD3, 0xF6, 0x9A, 0x8B, + 0xB0, 0xF7, 0x42, 0x24, 0xE2, 0xB4, 0x4E, 0x33, + 0x57, 0x81, 0x22, 0x11, 0xD2, 0xE6, 0x2E, 0xFB, + 0xC9, 0x1B, 0xB5, 0x53, 0x09, 0x8E, 0x25, 0xE3, + 0x3A, 0x79, 0x9A, 0xDC, 0x7F, 0x76, 0xFE, 0xB2, + 0x08, 0xDA, 0x7C, 0x65, 0x22, 0xCD, 0xB0, 0x71, + 0x9A, 0x30, 0x51, 0x80, 0xCC, 0x54, 0xA8, 0x2E + }; + var compressedPublicKey = new byte[] + { + 0x02, + 0xEB, 0xCA, 0x94, 0xD7, 0x33, 0xE3, 0x99, 0xB2, + 0xDB, 0x96, 0xEA, 0xCD, 0xD3, 0xF6, 0x9A, 0x8B, + 0xB0, 0xF7, 0x42, 0x24, 0xE2, 0xB4, 0x4E, 0x33, + 0x57, 0x81, 0x22, 0x11, 0xD2, 0xE6, 0x2E, 0xFB + }; + + EcdhProvider ecdhProvider = new EcdhProvider(EllipticCurve.Prime256V1); + + var unpacked1 = ecdhProvider.UnpackPublic(uncompressedPublicKey); + var unpacked2 = ecdhProvider.UnpackPublic(compressedPublicKey); + + if (unpacked1.X == unpacked2.X && unpacked1.Y == unpacked2.Y) + { + Console.WriteLine("UnpackPublic() works correctly"); + } + else + { + Console.WriteLine("UnpackPublic() does not work correctly"); + } + } + { + // from https://github.com/KonataDev/Konata.Core/blob/develop/Konata.Core.Test/UtilTest/EcdhTest.cs + var uncompressedPublicKey = new byte[] + { + 0x04, + 0xD5, 0xCF, 0xB0, 0x2D, 0x5D, 0x4F, 0xCA, 0x2C, + 0x84, 0xF6, 0xF1, 0x29, 0x4B, 0x45, 0x5B, 0xAB, + 0x4C, 0x96, 0x98, 0xDD, 0x57, 0x2B, 0xF8, 0x63, + 0x82, 0xA9, 0xDA, 0xF8, 0xAD, 0xE9, 0xD4, 0x5A, + 0x57, 0xDE, 0x14, 0x04, 0xFA, 0x5D, 0x41, 0x29, + 0x1E, 0x0A, 0x56, 0xCB, 0x45, 0x08, 0xD3, 0x2F + }; + var compressedPublicKey = new byte[] + { + 0x03, + 0xD5, 0xCF, 0xB0, 0x2D, 0x5D, 0x4F, 0xCA, 0x2C, + 0x84, 0xF6, 0xF1, 0x29, 0x4B, 0x45, 0x5B, 0xAB, + 0x4C, 0x96, 0x98, 0xDD, 0x57, 0x2B, 0xF8, 0x63 + }; + + EcdhProvider ecdhProvider = new EcdhProvider(EllipticCurve.Secp192K1); + + var unpacked1 = ecdhProvider.UnpackPublic(uncompressedPublicKey); + var unpacked2 = ecdhProvider.UnpackPublic(compressedPublicKey); + + if (unpacked1.X == unpacked2.X && unpacked1.Y == unpacked2.Y) + { + Console.WriteLine("UnpackPublic() works correctly"); + } + else + { + Console.WriteLine("UnpackPublic() does not work correctly"); + } + } + } +} + + diff --git a/Lagrange.Core/Lagrange.Core.Test/Tests/NTLoginTest.cs b/Lagrange.Core/Lagrange.Core.Test/Tests/NTLoginTest.cs new file mode 100644 index 0000000..fbc45fc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Tests/NTLoginTest.cs @@ -0,0 +1,65 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Interface; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.Core.Message; + +namespace Lagrange.Core.Test.Tests; + +// ReSharper disable once InconsistentNaming + +public class NTLoginTest +{ + public async Task LoginByPassword() + { + var deviceInfo = WtLoginTest.GetDeviceInfo(); + var keyStore = WtLoginTest.LoadKeystore(); + + if (keyStore == null) + { + Console.WriteLine("Please login by QrCode first"); + return; + } + + var bot = BotFactory.Create(new BotConfig() + { + UseIPv6Network = false, + GetOptimumServer = true, + AutoReconnect = true, + Protocol = Protocols.Linux + }, deviceInfo, keyStore); + + bot.Invoker.OnBotLogEvent += (_, @event) => + { + Utility.Console.ChangeColorByTitle(@event.Level); + Console.WriteLine(@event.ToString()); + }; + + bot.Invoker.OnBotOnlineEvent += (_, @event) => + { + Console.WriteLine(@event.ToString()); + WtLoginTest.SaveKeystore(bot.UpdateKeystore()); + }; + + bot.Invoker.OnBotCaptchaEvent += (_, @event) => + { + Console.WriteLine(@event.ToString()); + var captcha = Console.ReadLine(); + var randStr = Console.ReadLine(); + if (captcha != null && randStr != null) bot.SubmitCaptcha(captcha, randStr); + }; + + bot.Invoker.OnGroupInvitationReceived += (_, @event) => + { + Console.WriteLine(@event.ToString()); + }; + + await bot.LoginByPassword(); + + var friendChain = MessageBuilder.Group(411240674) + .Text("This is the friend message sent by Lagrange.Core") + .Mention(1925648680); + await bot.SendMessage(friendChain.Build()); + + await Task.Delay(1000); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Tests/WtLoginTest.cs b/Lagrange.Core/Lagrange.Core.Test/Tests/WtLoginTest.cs new file mode 100644 index 0000000..08d519c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Tests/WtLoginTest.cs @@ -0,0 +1,79 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Lagrange.Core.Common; +using Lagrange.Core.Common.Interface; +using Lagrange.Core.Common.Interface.Api; + +namespace Lagrange.Core.Test.Tests; + +public class WtLoginTest +{ + public async Task FetchQrCode() + { + var deviceInfo = GetDeviceInfo(); + var keyStore = LoadKeystore() ?? new BotKeystore(); + + var bot = BotFactory.Create(new BotConfig + { + UseIPv6Network = false, + GetOptimumServer = true, + AutoReconnect = true, + Protocol = Protocols.Linux + }, deviceInfo, keyStore); + + bot.Invoker.OnBotLogEvent += (context, @event) => + { + Utility.Console.ChangeColorByTitle(@event.Level); + Console.WriteLine(@event.ToString()); + }; + + bot.Invoker.OnBotOnlineEvent += (context, @event) => + { + Console.WriteLine(@event.ToString()); + SaveKeystore(bot.UpdateKeystore()); + }; + + var qrCode = await bot.FetchQrCode(); + if (qrCode != null) + { + await File.WriteAllBytesAsync("qr.png", qrCode.Value.QrCode); + await bot.LoginByQrCode(); + } + } + + public static BotDeviceInfo GetDeviceInfo() + { + if (File.Exists("Test/DeviceInfo.json")) + { + var info = JsonSerializer.Deserialize(File.ReadAllText("Test/DeviceInfo.json")); + if (info != null) return info; + + info = BotDeviceInfo.GenerateInfo(); + File.WriteAllText("Test/DeviceInfo.json", JsonSerializer.Serialize(info)); + return info; + } + + var deviceInfo = BotDeviceInfo.GenerateInfo(); + File.WriteAllText("Test/DeviceInfo.json", JsonSerializer.Serialize(deviceInfo)); + return deviceInfo; + } + + public static void SaveKeystore(BotKeystore keystore) => + File.WriteAllText("Test/Keystore.json", JsonSerializer.Serialize(keystore)); + + public static BotKeystore? LoadKeystore() + { + try + { + var text = File.ReadAllText("Test/Keystore.json"); + return JsonSerializer.Deserialize(text, new JsonSerializerOptions() + { + ReferenceHandler = ReferenceHandler.Preserve + }); + } + catch + { + return null; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Utility/Console.cs b/Lagrange.Core/Lagrange.Core.Test/Utility/Console.cs new file mode 100644 index 0000000..5cf1953 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Utility/Console.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Event.EventArg; + +namespace Lagrange.Core.Test.Utility; + +public static class Console +{ + public static void ChangeColorByTitle(this LogLevel level) => System.Console.ForegroundColor = level switch + { + LogLevel.Debug => ConsoleColor.White, + LogLevel.Verbose => ConsoleColor.DarkGray, + LogLevel.Information => ConsoleColor.Blue, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Fatal => ConsoleColor.Red, + _ => System.Console.ForegroundColor + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Utility/ProtoGen.cs b/Lagrange.Core/Lagrange.Core.Test/Utility/ProtoGen.cs new file mode 100644 index 0000000..ac95dc3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Utility/ProtoGen.cs @@ -0,0 +1,61 @@ +using System.Reflection; +using System.Text; +using ProtoBuf; + +namespace Lagrange.Core.Test.Utility; + +internal static class ProtoGen +{ + public static void GenerateProtoFiles() + { + var assembly = typeof(Lagrange.Core.Utility.ServiceInjector).Assembly; + var types = assembly.GetTypes(); + var sb = new StringBuilder(); + + sb.AppendLine("syntax = \"proto3\";"); + sb.AppendLine(); + sb.AppendLine("package Lagrange.Core;"); + + foreach (var type in types) + { + if (type.Namespace?.StartsWith("Lagrange.Core.Internal.Packets") != true) continue; + + sb.AppendLine($"message {type.Name} {{"); + var properties = type.GetProperties(); + foreach (var property in properties) + { + string typeString = ParseType(property.PropertyType); + sb.AppendLine($" {GetLastToken(typeString, '.')} {property.Name} = {property.GetCustomAttribute()?.Tag};"); + } + sb.AppendLine("}"); + sb.AppendLine(); + } + + string proto = sb.ToString(); + + File.WriteAllText("packets.proto", proto); + } + + private static string ParseType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + { + return $"repeated {ParseType(type.GetGenericArguments()[0])}"; + } + + return type.ToString() switch + { + "System.UInt64" => "varint", + "System.UInt32" => "varint", + "System.UInt16" => "varint", + "System.Int64" => "varint", + "System.Int32" => "varint", + "System.String" => "string", + "System.Boolean" => "bool", + "System.Byte[]" => "bytes", + _ => type.ToString() + }; + } + + private static string GetLastToken(string str, char separator) => str.Split(separator)[^1]; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.Test/Utility/Tlv.cs b/Lagrange.Core/Lagrange.Core.Test/Utility/Tlv.cs new file mode 100644 index 0000000..00d5d0f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.Test/Utility/Tlv.cs @@ -0,0 +1,35 @@ +using System.Text; +using Lagrange.Core.Utility.Extension; +using static Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Test.Utility; + +public static class Tlv +{ + public static Dictionary GetTlvDictionary(byte[] tlvs, bool isCommand = true) + { + var result = new Dictionary(); + + using var reader = new BinaryReader(new MemoryStream(tlvs)); + + ushort command; + if (isCommand) + { + command = ToUInt16(reader.ReadBytes(2), false); + } + + ushort tlvCount = ToUInt16(reader.ReadBytes(2), false); + + for (int i = 0; i < tlvCount; i++) + { + ushort tlvTag = ToUInt16(reader.ReadBytes(2), false); + ushort tlvLength = ToUInt16(reader.ReadBytes(2), false); + byte[] tlvValue = reader.ReadBytes(tlvLength); + + result.Add($"0x{tlvTag:X} {tlvLength}", tlvValue.Hex()); + result.Add($"0x{tlvTag:X} UTF8 {tlvLength}", Encoding.UTF8.GetString(tlvValue)); + } + + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.sln b/Lagrange.Core/Lagrange.Core.sln new file mode 100644 index 0000000..3723ed1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lagrange.Core", "Lagrange.Core\Lagrange.Core.csproj", "{909C99CC-0CB7-4A34-8C75-AD25E6AEA535}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lagrange.Core.Test", "Lagrange.Core.Test\Lagrange.Core.Test.csproj", "{D64B6BAB-CD20-4660-8A6E-BCC936652204}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lagrange.OneBot", "Lagrange.OneBot\Lagrange.OneBot.csproj", "{37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "SolutionItem", "{ACE96E15-65D1-471B-913A-A1014F0D003E}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + README_zh.md = README_zh.md + Docker.md = Docker.md + Docker_zh.md = Docker_zh.md + LICENSE = LICENSE + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Debug|Any CPU.Build.0 = Debug|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Release|Any CPU.ActiveCfg = Release|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Release|Any CPU.Build.0 = Release|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Release|Any CPU.Build.0 = Release|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Lagrange.Core/Lagrange.Core/AssemblyInfo.cs b/Lagrange.Core/Lagrange.Core/AssemblyInfo.cs new file mode 100644 index 0000000..6c8cabd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Lagrange.Core.Test")] // Allow unit test to access internal members +[assembly: InternalsVisibleTo("Lagrange.OneBot")] // OneBot Implementation diff --git a/Lagrange.Core/Lagrange.Core/BotContext.cs b/Lagrange.Core/Lagrange.Core/BotContext.cs new file mode 100644 index 0000000..45ef3df --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/BotContext.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Event; +using Lagrange.Core.Internal.Context; + +namespace Lagrange.Core; + +public class BotContext : IDisposable +{ + public readonly EventInvoker Invoker; + + public uint BotUin => ContextCollection.Keystore.Uin; + + public string? BotName => ContextCollection.Keystore.Info?.Name; + + internal readonly Utility.TaskScheduler Scheduler; + + internal readonly ContextCollection ContextCollection; + + public BotAppInfo AppInfo { get; } + + public BotConfig Config { get; } + + private readonly BotDeviceInfo _deviceInfo; + + private readonly BotKeystore _keystore; + + internal BotContext(BotConfig config, BotDeviceInfo deviceInfo, BotKeystore keystore, BotAppInfo appInfo) + { + Invoker = new EventInvoker(this); + Scheduler = new Utility.TaskScheduler(); + + Config = config; + AppInfo = appInfo; + _deviceInfo = deviceInfo; + _keystore = keystore; + + ContextCollection = new ContextCollection(_keystore, AppInfo, _deviceInfo, Config, Invoker, Scheduler); + } + + public void Dispose() + { + ContextCollection.Dispose(); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/BotAppInfo.cs b/Lagrange.Core/Lagrange.Core/Common/BotAppInfo.cs new file mode 100644 index 0000000..22d48ea --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/BotAppInfo.cs @@ -0,0 +1,109 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Common; + +public class BotAppInfo +{ + public string Os { get; set; } + + public string VendorOs { get; set; } + + public string Kernel { get; set; } + + public string CurrentVersion { get; set; } + + public int MiscBitmap { get; set; } + + public string PtVersion { get; set; } + + public int SsoVersion { get; set; } + + public string PackageName { get; set; } + + public string WtLoginSdk { get; set; } + + public int AppId { get; set; } + + /// Or known as pubId in tencent log + public int SubAppId { get; set; } + + public int AppIdQrCode { get; set; } + + public ushort AppClientVersion { get; set; } + + public uint MainSigMap { get; set; } + + public ushort SubSigMap { get; set; } + + public ushort NTLoginType { get; set; } + + private static readonly BotAppInfo Linux = new() + { + Os = "Linux", + Kernel = "Linux", + VendorOs = "linux", + CurrentVersion = "3.2.15-30366", + MiscBitmap = 32764, + PtVersion = "2.0.0", + SsoVersion = 19, + PackageName = "com.tencent.qq", + WtLoginSdk = "nt.wtlogin.0.0.1", + AppId = 1600001615, + SubAppId = 537258424, + AppIdQrCode = 13697054, + AppClientVersion = 30366, + + MainSigMap = 169742560, + SubSigMap = 0, + NTLoginType = 1 + }; + + private static readonly BotAppInfo MacOs = new() + { + Os = "Mac", + Kernel = "Darwin", + VendorOs = "mac", + CurrentVersion = "6.9.23-20139", + PtVersion = "2.0.0", + MiscBitmap = 32764, + SsoVersion = 23, + PackageName = "com.tencent.qq", + WtLoginSdk = "nt.wtlogin.0.0.1", + AppId = 1600001602, + SubAppId = 537200848, + AppIdQrCode = 537200848, + AppClientVersion = 13172, + + MainSigMap = 169742560, + SubSigMap = 0, + NTLoginType = 5 + }; + + private static readonly BotAppInfo Windows = new() + { + Os = "Windows", + Kernel = "Windows_NT", + VendorOs = "win32", + CurrentVersion = "9.9.2-15962", + PtVersion = "2.0.0", + MiscBitmap = 32764, + SsoVersion = 23, + PackageName = "com.tencent.qq", + WtLoginSdk = "nt.wtlogin.0.0.1", + AppId = 1600001604, + SubAppId = 537138217, + AppIdQrCode = 537138217, + AppClientVersion = 13172, + + MainSigMap = 169742560, + SubSigMap = 0, + NTLoginType = 5 + }; + + public static readonly Dictionary ProtocolToAppInfo = new() + { + { Protocols.Windows, Windows }, + { Protocols.Linux, Linux }, + { Protocols.MacOs, MacOs }, + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/BotConfig.cs b/Lagrange.Core/Lagrange.Core/Common/BotConfig.cs new file mode 100644 index 0000000..6cac812 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/BotConfig.cs @@ -0,0 +1,60 @@ +using Lagrange.Core.Utility.Sign; + +namespace Lagrange.Core.Common; + +/// +/// Configuration for The bot client +/// +[Serializable] +public class BotConfig +{ + /// + /// The protocol for the client, default is Linux + /// + public Protocols Protocol { get; set; } = Protocols.Linux; + + /// + /// Auto reconnect to server when disconnected + /// + public bool AutoReconnect { get; set; } = true; + + /// + /// Use the IPv6 to connect to server, only if your network support IPv6 + /// + public bool UseIPv6Network { get; set; } = false; + + /// + /// Get optimum server from Tencent MSF server, set to false to use hardcode server + /// + public bool GetOptimumServer { get; set; } = true; + + /// + /// Custom Sign Provider + /// + public SignProvider? CustomSignProvider { get; set; } = null; + + /// + /// The maximum size of the highway block in byte, max 1MB (1024 * 1024 byte) + /// + public uint HighwayChunkSize { get; set; } = 1024 * 1024; + + /// + /// Highway Uploading Concurrency, if the image failed to send, set this to 1 + /// + public uint HighwayConcurrent { get; set; } = 4; + + /// + /// Refresh the session when the session is about to expired + /// + public bool AutoReLogin { get; set; } = true; +} + +/// +/// The Protocol for the client +/// +public enum Protocols +{ + Windows = 0, + MacOs = 1, + Linux = 2 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/BotDeviceInfo.cs b/Lagrange.Core/Lagrange.Core/Common/BotDeviceInfo.cs new file mode 100644 index 0000000..7d5703c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/BotDeviceInfo.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Utility.Generator; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Common; + +[Serializable] +public class BotDeviceInfo +{ + public Guid Guid { get; set; } + + public byte[] MacAddress { get; set; } + + public string DeviceName { get; set; } + + public string SystemKernel { get; set; } + + public string KernelVersion { get; set; } + + public static BotDeviceInfo GenerateInfo() => new() + { + Guid = Guid.NewGuid(), + MacAddress = ByteGen.GenRandomBytes(6), + DeviceName = $"Lagrange-{StringGen.GenerateHex(6).ToUpper()}", + SystemKernel = "Windows 10.0.19042", + KernelVersion = "10.0.19042.0" + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/BotKeystore.cs b/Lagrange.Core/Lagrange.Core/Common/BotKeystore.cs new file mode 100644 index 0000000..0e8845f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/BotKeystore.cs @@ -0,0 +1,131 @@ +using System.Text; +using System.Text.Json.Serialization; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; + + +namespace Lagrange.Core.Common; + +public class BotKeystore +{ + [JsonConstructor] + public BotKeystore() + { + PasswordMd5 = ""; + + SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + PrimeImpl = new EcdhImpl(EcdhImpl.CryptMethod.Prime256V1, false); + TeaImpl = new TeaImpl(); + + Stub = new KeyCollection(); + + var tempPwd = Session?.TempPassword; + Session = tempPwd != null ? new WtLoginSession { TempPassword = tempPwd } : new WtLoginSession(); + } + + /// + /// Create the Bot keystore + /// + /// Set this field 0 to use QrCode Login + /// Password Raw + internal BotKeystore(uint uin, string password) + { + Uin = uin; + PasswordMd5 = Encoding.UTF8.GetBytes(password).Md5(); + + SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + PrimeImpl = new EcdhImpl(EcdhImpl.CryptMethod.Prime256V1, false); + TeaImpl = new TeaImpl(); + Session = new WtLoginSession(); + + Stub = new KeyCollection(); + } + + public uint Uin { get; set; } + + public string? Uid { get; set; } + + public string PasswordMd5 { get; set; } + + internal EcdhImpl SecpImpl { get; set; } + internal EcdhImpl PrimeImpl { get; set; } + internal TeaImpl TeaImpl { get; set; } + + internal KeyCollection Stub { get; } + + public WtLoginSession Session { get; set; } + + public BotInfo? Info { get; set; } + + [Serializable] + public class KeyCollection + { + public byte[] RandomKey { get; set; } = ByteGen.GenRandomBytes(16); + public byte[] TgtgtKey { get; set; } = new byte[16]; + } + + [Serializable] + public class WtLoginSession + { + public byte[] D2Key { get; set; } = new byte[16]; + public byte[] D2 { get; set; } = Array.Empty(); + public byte[] Tgt { get; set; } = Array.Empty(); + + public DateTime SessionDate { get; set; } + + internal byte[]? QrSign { get; set; } // size: 24 + internal string? QrString { get; set; } + internal string? QrUrl { get; set; } + + internal byte[]? ExchangeKey { get; set; } + internal byte[]? KeySign { get; set; } + internal byte[]? UnusualSign { get; set; } + internal string? UnusualCookies { get; set; } + internal string? CaptchaUrl { get; set; } + internal string? NewDeviceVerifyUrl { get; set; } + internal (string, string, string)? Captcha { get; set; } + + public byte[]? TempPassword { get; set; } + internal byte[]? NoPicSig { get; set; } // size: 16, may be from Tlv19, for Tlv16A + + private ushort _sequence; + internal ushort Sequence + { + get => _sequence++; + set => _sequence = value; + } + } + + [Serializable] + public class BotInfo + { + internal BotInfo(byte age, byte gender, string name) + { + Age = age; + Gender = gender; + Name = name; + } + + [JsonConstructor] + public BotInfo() + { + Name = ""; + } + + public byte Age { get; set; } + + public byte Gender { get; set; } + + public string Name { get; set; } + + public override string ToString() => $"Bot name: {Name} | Gender: {Gender} | Age: {Age}"; + } + + internal void ClearSession() + { + Session.D2 = Array.Empty(); + Session.Tgt = Array.Empty(); + Session.D2Key = new byte[16]; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/BotScheduler.cs b/Lagrange.Core/Lagrange.Core/Common/BotScheduler.cs new file mode 100644 index 0000000..36176bf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/BotScheduler.cs @@ -0,0 +1,86 @@ +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable UnusedAutoPropertyAccessor.Local + +namespace Lagrange.Core.Common; + +/// +/// Task Scheduler +/// +public class Scheduler +{ + public const int Infinity = int.MaxValue; + + private BotContext Bot { get; } + + private Utility.TaskScheduler Instance { get; } + + public string Name { get; } + + public Action Action { get; } + + internal Scheduler(BotContext bot, string name, Action action) + { + Bot = bot; + Name = name; + Action = action; + Instance = bot.Scheduler; + } + + ~Scheduler() => Cancel(); + + /// + /// Create a task scheduler + /// + /// [In] Bot instance + /// [In] Task identity name + /// [In] Task callback action + /// + public static Scheduler Create(BotContext bot, string name, Action action) => new(bot, name, action); + + /// + /// Execute the task with a specific interval + /// + /// [In] Interval in milliseconds + /// [In] Execute times + /// + /// + public void Interval(int interval, int times) => Instance.Interval(Name, interval, times, () => Action(Bot)); + + /// + /// Execute the task with a specific interval infinity + /// + /// [In] Interval in milliseconds + /// + /// + public void Interval(int interval) => Instance.Interval(Name, interval, Infinity, () => Action(Bot)); + + /// + /// Execute the task once + /// + /// [In] Delay time in milliseconds + /// + /// + public void RunOnce(int delay) => Instance.RunOnce(Name, delay, () => Action(Bot)); + + /// + /// Execute the task once + /// + /// [In] Execute date + /// + /// + public void RunOnce(DateTime date) => Instance.RunOnce(Name, date, () => Action(Bot)); + + /// + /// Trigger a task to run + /// + public void Trigger() => Instance.Trigger(Name); + + /// + /// Cancel the task + /// + /// + public void Cancel() => Instance.Cancel(Name); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/AiCharacter.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/AiCharacter.cs new file mode 100644 index 0000000..50e9081 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/AiCharacter.cs @@ -0,0 +1,32 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class AiCharacter +{ + public string VoiceId { get; set; } + + public string CharacterName { get; set; } + + public string CharacterVoiceUrl { get; set; } + + public AiCharacter(string voiceId, string characterName, string characterVoiceUrl) + { + VoiceId = voiceId; + CharacterName = characterName; + CharacterVoiceUrl = characterVoiceUrl; + } +} + +[Serializable] +public class AiCharacterList +{ + public string Type { get; set; } + + public List Characters { get; set; } + + public AiCharacterList(string type, List characters) + { + Type = type; + Characters = characters; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotBusiness.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotBusiness.cs new file mode 100644 index 0000000..9d9b7d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotBusiness.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Common.Entity +{ + public class BusinessCustom + { + public uint Type; + + public uint Level; + + public string? Icon; + + public uint IsYear; + + public uint IsPro; + } + + public class BusinessCustomList + { + public List BusinessLists { get; set; } + + public BusinessCustomList(List businessLists) + { + BusinessLists = businessLists; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotCustomStatus.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotCustomStatus.cs new file mode 100644 index 0000000..7762820 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotCustomStatus.cs @@ -0,0 +1,10 @@ +namespace Lagrange.Core.Common.Entity; + +public class BotStatus +{ + public uint StatusId { get; set; } + + public uint? FaceId { get; set; } + + public string? Msg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotFileEntry.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFileEntry.cs new file mode 100644 index 0000000..393e8e6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFileEntry.cs @@ -0,0 +1,37 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotFileEntry : IBotFSEntry +{ + public string FileId { get; } + + public string FileName { get; } + + public string ParentDirectory { get; } + + public ulong FileSize { get; } + + public DateTime ExpireTime { get; } + + public DateTime ModifiedTime { get; } + + public uint UploaderUin { get; } + + public DateTime UploadedTime { get; } + + public uint DownloadedTimes { get; } + + internal BotFileEntry(string fileId, string fileName, string parentDirectory, ulong fileSize, + DateTime expireTime, DateTime modifiedTime, uint uploaderUin, DateTime uploadedTime, uint downloadedTimes) + { + FileId = fileId; + FileName = fileName; + ParentDirectory = parentDirectory; + FileSize = fileSize; + ExpireTime = expireTime; + ModifiedTime = modifiedTime; + UploaderUin = uploaderUin; + UploadedTime = uploadedTime; + DownloadedTimes = downloadedTimes; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotFolderEntry.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFolderEntry.cs new file mode 100644 index 0000000..c5b8139 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFolderEntry.cs @@ -0,0 +1,31 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotFolderEntry : IBotFSEntry +{ + public string FolderId { get; set; } + + public string ParentFolderId { get; set; } + + public string FolderName { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime ModifiedTime { get; set; } + + public uint CreatorUin { get; set; } + + public uint TotalFileCount { get; set; } + + internal BotFolderEntry(string folderId, string parentFolderId, string folderName, DateTime createTime, + DateTime modifiedTime, uint creatorUin, uint totalFileCount) + { + FolderId = folderId; + ParentFolderId = parentFolderId; + FolderName = folderName; + CreateTime = createTime; + ModifiedTime = modifiedTime; + CreatorUin = creatorUin; + TotalFileCount = totalFileCount; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriend.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriend.cs new file mode 100644 index 0000000..8290efa --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriend.cs @@ -0,0 +1,47 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotFriend +{ + /// + /// The empty constructor for serialization + /// + internal BotFriend() + { + Uid = string.Empty; + Nickname = string.Empty; + Remarks = string.Empty; + PersonalSign = string.Empty; + Qid = string.Empty; + Group = default; + Avatar = string.Empty; + } + + internal BotFriend(uint uin, string uid, string nickname, string remarks, string personalSign, string qid, BotFriendGroup group = default) + { + Uin = uin; + Uid = uid; + Nickname = nickname; + Remarks = remarks; + PersonalSign = personalSign; + Qid = qid; + Group = group; + Avatar = $"https://q1.qlogo.cn/g?b=qq&nk={uin}&s=640"; + } + + public uint Uin { get; set; } + + internal string Uid { get; set; } + + public string Nickname { get; set; } + + public string Remarks { get; set; } + + public string PersonalSign { get; set; } + + public string Qid { get; set; } + + public BotFriendGroup Group { get; set; } + + public string Avatar { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendGroup.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendGroup.cs new file mode 100644 index 0000000..9509773 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendGroup.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public struct BotFriendGroup +{ + public uint GroupId { get; } + + public string GroupName { get; } + + internal BotFriendGroup(uint groupId, string groupName) + { + GroupId = groupId; + GroupName = groupName; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendRequest.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendRequest.cs new file mode 100644 index 0000000..78dc009 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotFriendRequest.cs @@ -0,0 +1,39 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotFriendRequest +{ + public BotFriendRequest(string targetUid, string sourceUid, uint eventState, string comment, string source, uint time) + { + TargetUid = targetUid; + SourceUid = sourceUid; + EventState = (State)eventState; + Comment = comment; + Source = source; + Time = DateTime.UnixEpoch.AddSeconds(time); + } + + public string TargetUid { get; set; } + + public uint TargetUin { get; set; } + + public string SourceUid { get; set; } + + public uint SourceUin { get; set; } + + public State EventState { get; set; } + + + public string Comment { get; set; } + + public string Source { get; set; } + + public DateTime Time { get; set; } + + public enum State + { + Pending = 1, + Disapproved = 2, + Approved = 3, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGetGroupTodoResult.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGetGroupTodoResult.cs new file mode 100644 index 0000000..d47cf39 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGetGroupTodoResult.cs @@ -0,0 +1,24 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotGetGroupTodoResult +{ + public int Retcode { get; } + + public string? ResultMessage { get; } + + public uint GroupUin { get; } + + public uint Sequence { get; } + + public string Preview { get; } + + public BotGetGroupTodoResult(int retcode, string? resultMessage, uint groupUin, uint sequence, string preview) + { + Retcode = retcode; + ResultMessage = resultMessage; + GroupUin = groupUin; + Sequence = sequence; + Preview = preview; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroup.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroup.cs new file mode 100644 index 0000000..b30c028 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroup.cs @@ -0,0 +1,35 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotGroup +{ + internal BotGroup(uint groupUin, string groupName, uint memberCount, uint maxMember, uint createTime, string? description, string? question, string? announcement) + { + GroupUin = groupUin; + GroupName = groupName; + MemberCount = memberCount; + MaxMember = maxMember; + CreateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(createTime); + Description = description; + Question = question; + Announcement = announcement; + } + + public uint GroupUin { get; } + + public string GroupName { get; } + + public uint MemberCount { get; } + + public uint MaxMember { get; } + + public DateTime CreateTime { get; } + + public string? Description { get; } + + public string? Question { get; } + + public string? Announcement { get; } + + public string Avatar => $"https://p.qlogo.cn/gh/{GroupUin}/{GroupUin}/0/"; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupClockInResult.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupClockInResult.cs new file mode 100644 index 0000000..1520d32 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupClockInResult.cs @@ -0,0 +1,52 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotGroupClockInResult +{ + public BotGroupClockInResult() { } + + public BotGroupClockInResult(bool isSuccess) + { + IsSuccess = isSuccess; + } + + /// + /// Is the clock in successful + /// + public bool IsSuccess { get; set; } = false; + + /// + /// Maybe "今日已成功打卡" + /// + public string Title { get; set; } = string.Empty; + + /// + /// Maybe "已打卡N天" + /// + public string KeepDayText { get; set; } = string.Empty; + + /// + /// Maybe "群内排名第N位" + /// + public string GroupRankText { get; set; } = string.Empty; + + /// + /// The utc time of clock in + /// + public DateTime ClockInUtcTime { get; set; } = DateTime.UnixEpoch; // 打卡时间 + + /// + /// Detail info url + /// + public string DetailUrl { get; set; } = string.Empty; // https://qun.qq.com/v2/signin/detail?... + + public static BotGroupClockInResult Fail() => new BotGroupClockInResult() + { + IsSuccess = false + }; + + public static BotGroupClockInResult Success() => new BotGroupClockInResult() + { + IsSuccess = true + }; +} diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupInfo.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupInfo.cs new file mode 100644 index 0000000..2cde017 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupInfo.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Common.Entity; + +public class BotGroupInfo +{ + public string OwnerUid { get; set; } = string.Empty; + + public ulong CreateTime { get; set; } + + public ulong MaxMemberCount { get; set; } + + public ulong MemberCount { get; set; } + + public ulong Level { get; set; } + + public string Name { get; set; } = string.Empty; + + public string NoticePreview { get; set; } = string.Empty; + + public ulong Uin { get; set; } + + public ulong LastSequence { get; set; } + + public ulong LastMessageTime { get; set; } + + public string Question { get; set; } = string.Empty; + + public string Answer { get; set; } = string.Empty; + + public ulong MaxAdminCount { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupMember.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupMember.cs new file mode 100644 index 0000000..190d3c4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupMember.cs @@ -0,0 +1,60 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotGroupMember +{ + /// + /// The empty constructor for serialization + /// + internal BotGroupMember() + { + Uid = string.Empty; + MemberCard = string.Empty; + MemberName = string.Empty; + SpecialTitle = string.Empty; + } + + internal BotGroupMember(uint uin, string uid, GroupMemberPermission permission, uint groupLevel, string? memberCard, + string memberName, string? specialTitle, DateTime joinTime, DateTime lastMsgTime,DateTime shutUpTimestamp) + { + Uin = uin; + Uid = uid; + Permission = permission; + GroupLevel = groupLevel; + MemberCard = memberCard; + MemberName = memberName; + SpecialTitle = specialTitle; + JoinTime = joinTime; + LastMsgTime = lastMsgTime; + ShutUpTimestamp = shutUpTimestamp; + } + + public uint Uin { get; set; } + + internal string Uid { get; set; } + + public GroupMemberPermission Permission { get; set; } + + public uint GroupLevel { get; set; } + + public string? MemberCard { get; set; } + + public string MemberName { get; set; } + + public string? SpecialTitle { get; set; } + + public DateTime JoinTime { get; set; } + + public DateTime LastMsgTime { get; set; } + + public DateTime ShutUpTimestamp { get; set; } + + public string Avatar => $"https://q1.qlogo.cn/g?b=qq&nk={Uin}&s=640"; +} + +public enum GroupMemberPermission : uint +{ + Member = 0, + Owner = 1, + Admin = 2, +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupReaction.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupReaction.cs new file mode 100644 index 0000000..ec0ebdf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupReaction.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Common.Entity; + +public class BotGroupReaction +{ + public string FaceId { get; set; } + + public uint Type { get; set; } + + public uint Count { get; set; } + + public bool IsAdded { get; set; } + + public BotGroupReaction(string faceId, uint type, uint count, bool isAdded) + { + FaceId = faceId; + Type = type; + Count = count; + IsAdded = isAdded; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupRequest.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupRequest.cs new file mode 100644 index 0000000..b54fc6b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotGroupRequest.cs @@ -0,0 +1,75 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotGroupRequest +{ + internal BotGroupRequest( + uint groupUin, + uint? invitorMemberUin, + string? invitorMemberCard, + uint targetMemberUin, + string targetMemberCard, + uint? operatorUin, + string? operatorName, + uint state, + ulong sequence, + uint type, + string? comment, + bool isFiltered) + { + GroupUin = groupUin; + InvitorMemberUin = invitorMemberUin; + InvitorMemberCard = invitorMemberCard; + TargetMemberUin = targetMemberUin; + TargetMemberCard = targetMemberCard; + OperatorUin = operatorUin; + OperatorName = operatorName; + EventState = (State)state; + Sequence = sequence; + EventType = (Type)type; + Comment = comment; + IsFiltered = isFiltered; + } + + public uint GroupUin { get; set; } + + public uint? InvitorMemberUin { get; set; } + + public string? InvitorMemberCard { get; set; } + + public uint TargetMemberUin { get; set; } + + public string TargetMemberCard { get; set; } + + public uint? OperatorUin { get; set; } + + public string? OperatorName { get; set; } + + public Type EventType { get; set; } + + public State EventState { get; set; } + + internal ulong Sequence { get; set; } // for internal use of Approving Requests + + public string? Comment { get; } + + public bool IsFiltered { get; set; } + + public enum State + { + Default = 0, + Pending = 1, + Approved = 2, + Disapproved = 3, + } + + public enum Type + { + GroupRequest = 1, + SelfInvitation = 2, + KickMember = 6, + KickSelf =7, + ExitGroup = 13, + GroupInvitation = 22, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/BotUserInfo.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/BotUserInfo.cs new file mode 100644 index 0000000..f6f6ba9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/BotUserInfo.cs @@ -0,0 +1,63 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class BotUserInfo +{ + internal BotUserInfo(uint uin, string nickname, string avatar, DateTime birthday, string city, string country, string school, uint age, DateTime registerTime, GenderInfo gender, string? qid, uint level, string sign, BotStatus status, List business) + { + Uin = uin; + Avatar = avatar; + // Avatar = $"https://q1.qlogo.cn/g?b=qq&nk={Uin}&s=640"; + Nickname = nickname; + Birthday = birthday; + City = city; + Country = country; + School = school; + Age = age; + RegisterTime = registerTime; + Gender = gender; + Qid = qid; + Level = level; + Sign = sign; + Status = status; + Business = business; + } + + public uint Uin { get; set; } + + public string Avatar { get; set; } + + public string Nickname { get; set; } + + public DateTime Birthday { get; set; } + + public string City { get; set; } + + public string Country { get; set; } + + public string School { get; set; } + + public uint Age { get; set; } + + public DateTime RegisterTime { get; set; } + + public GenderInfo Gender { get; set; } + + public string? Qid { get; set; } + + public uint Level { get; set; } + + public string Sign { get; set; } + + public BotStatus Status { get; set; } + + public List Business { get; set; } + + public enum GenderInfo + { + Unset = 0, + Male = 1, + Female = 2, + Unknown = 255 + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/IBotFSEntry.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/IBotFSEntry.cs new file mode 100644 index 0000000..437c595 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/IBotFSEntry.cs @@ -0,0 +1,6 @@ +namespace Lagrange.Core.Common.Entity; + +/// +/// Indicate that it is an element in BotFileSystem +/// +public interface IBotFSEntry { } \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs new file mode 100644 index 0000000..aa9f9aa --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.Core.Common.Entity +{ + [Serializable] + public class ImageOcrResult + { + [JsonPropertyName("texts")] public List Texts { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + public ImageOcrResult(List texts, string language) + { + Texts = texts; + Language = language; + } + } + + [Serializable] + public class TextDetection + { + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("confidence")] public int Confidence { get; set; } + + [JsonPropertyName("coordinates")] public List Coordinates { get; set; } + + public TextDetection(string text, int confidence, List coordinates) + { + Text = text; + Confidence = confidence; + Coordinates = coordinates; + } + } + + [Serializable] + public class Coordinate + { + [JsonPropertyName("x")] public int X { get; set; } + + [JsonPropertyName("y")] public int Y { get; set; } + + public Coordinate(int x, int y) + { + X = x; + Y = y; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/PokeFaceType.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/PokeFaceType.cs new file mode 100644 index 0000000..cfe9f3e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/PokeFaceType.cs @@ -0,0 +1,94 @@ +namespace Lagrange.Core.Common.Entity; + +public enum PokeFaceType +{ + Poke = 1, + Heart = 2, + Like = 3, + HeartBroken = 4, + SixSixSix = 5, + Kamehameha = 6 +} + +public enum SpecialPokeFaceType +{ + Like = 1, + Hear = 2, + Haha = 3, + Pig = 4, + Bomb = 5, + Poop = 6, + Mua = 7, + Pill = 8, + Durian = 9, + Lololo = 10, + Pan = 11, + Cash = 12, +} + +public static class PokeFaceTypeExt +{ + private static readonly string[] nameMapping = + { + string.Empty, "戳一戳", "比心", "点赞", "心碎", "666", "放大招" + }; + private static readonly string[] specialNameMapping = + { + string.Empty, "点赞", "爱心", "哈哈", "猪头", "炸弹", "便便", "亲亲", "药丸", "榴莲", "略略略", "平底锅", "钞票" + }; + + /// + /// Return the name of the shake face type. + /// A will be thrown if the type is not valid. + /// + /// Thrown when the type is not valid. + public static string ToName(this PokeFaceType type) + { + int v = (int)type; + if (v <= 0 || v >= nameMapping.Length) + throw new ArgumentOutOfRangeException(nameof(type), type, null); + return nameMapping[v]; + } + + + /// + /// Tries to get the name of the shake face type. + /// Returns null if the type is not valid. + /// + /// The shake face type. + /// The name of the shake face type, or null if the type is not valid. + public static string? TryGetName(this PokeFaceType type) + { + int v = (int)type; + if (v <= 0 || v >= nameMapping.Length) + return null; + return nameMapping[v]; + } + + /// + /// Return the name of the shake face type. + /// A will be thrown if the type is not valid. + /// + /// Thrown when the type is not valid. + public static string ToName(this SpecialPokeFaceType type) + { + int v = (int)type; + if (v <= 0 || v >= specialNameMapping.Length) + throw new ArgumentOutOfRangeException(nameof(type), type, null); + return specialNameMapping[v]; + } + + /// + /// Tries to get the name of the shake face type. + /// Returns null if the type is not valid. + /// + /// The shake face type. + /// The name of the shake face type, or null if the type is not valid. + public static string? TryGetName(this SpecialPokeFaceType type) + { + int v = (int)type; + if (v <= 0 || v >= specialNameMapping.Length) + return null; + return specialNameMapping[v]; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Entity/SysFaceEntry.cs b/Lagrange.Core/Lagrange.Core/Common/Entity/SysFaceEntry.cs new file mode 100644 index 0000000..7bbf046 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Entity/SysFaceEntry.cs @@ -0,0 +1,67 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class SysFaceEntry +{ + public string QSid { get; set; } + + public string? QDes { get; set; } + + public string? EMCode { get; set; } + + public int? QCid { get; set; } + + public int? AniStickerType { get; set; } + + public int? AniStickerPackId { get; set; } + + public int? AniStickerId { get; set; } + + public string? Url { get; set; } + + public string[]? EmojiNameAlias { get; set; } + + public int? AniStickerWidth { get; set; } + + public int? AniStickerHeight { get; set; } + + public SysFaceEntry(string qSid, string? qDes, string? emCode, int? qCid, int? aniStickerType, + int? aniStickerPackId, int? aniStickerId, string? url, string[]? emojiNameAlias, int? aniStickerWidth, + int? aniStickerHeight) + { + QSid = qSid; + QDes = qDes; + EMCode = emCode; + QCid = qCid; + AniStickerType = aniStickerType; + AniStickerPackId = aniStickerPackId; + AniStickerId = aniStickerId; + Url = url; + EmojiNameAlias = emojiNameAlias; + AniStickerWidth = aniStickerWidth; + AniStickerHeight = aniStickerHeight; + } +} + +[Serializable] +public class SysFacePackEntry +{ + public string EmojiPackName { get; set; } + + public SysFaceEntry[] Emojis { get; set; } + + public SysFacePackEntry(string emojiPackName, SysFaceEntry[] emojis) + { + EmojiPackName = emojiPackName; + Emojis = emojis; + } + + public uint[] GetUniqueSuperQSids((int AniStickerType, int AniStickerPackId)[] excludeAniStickerTypesAndPackIds) + => Emojis + .Where(e => e.AniStickerType is not null + && e.AniStickerPackId is not null + && !excludeAniStickerTypesAndPackIds.Contains((e.AniStickerType.Value, e.AniStickerPackId.Value))) + .Select(e => uint.Parse(e.QSid)) + .Distinct() + .ToArray(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Interface/Api/BotExt.cs b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/BotExt.cs new file mode 100644 index 0000000..4ae7895 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/BotExt.cs @@ -0,0 +1,55 @@ +using Lagrange.Core.Event; + +namespace Lagrange.Core.Common.Interface.Api; + +public static class BotExt +{ + /// + /// Fetch the qrcode for QRCode Login + /// + /// return url and qrcode image in PNG format + public static Task<(string Url, byte[] QrCode)?> FetchQrCode(this BotContext bot) + => bot.ContextCollection.Business.WtExchangeLogic.FetchQrCode(); + + /// + /// Use this method to login by QrCode, you should call first + /// + public static Task LoginByQrCode(this BotContext bot, CancellationToken cancellationToken = default) + => bot.ContextCollection.Business.WtExchangeLogic.LoginByQrCode(cancellationToken); + + /// + /// Use this method to login by password, EasyLogin may be preformed if there is sig in + /// + public static Task LoginByPassword(this BotContext bot, CancellationToken cancellationToken = default) + => bot.ContextCollection.Business.WtExchangeLogic.LoginByEasy(true, cancellationToken); + + /// + /// Use this method to login by easy, no fallback to password login/> + /// + public static Task LoginByEasy(this BotContext bot, CancellationToken cancellationToken = default) + => bot.ContextCollection.Business.WtExchangeLogic.LoginByEasy(false, cancellationToken); + + /// + /// Submit the captcha of the url given by the + /// + /// Whether the captcha is submitted successfully + public static bool SubmitCaptcha(this BotContext bot, string ticket, string randStr) + => bot.ContextCollection.Business.WtExchangeLogic.SubmitCaptcha(ticket, randStr); + + public static Task SetNeedToConfirmSwitch(this BotContext bot, bool needToConfirm) + => bot.ContextCollection.Business.OperationLogic.SetNeedToConfirmSwitch(needToConfirm); + + /// + /// Use this method to update keystore, so EasyLogin may be preformed next time by using this keystore + /// + /// BotKeystore instance + public static BotKeystore UpdateKeystore(this BotContext bot) + => bot.ContextCollection.Keystore; + + /// + /// Use this method to update device info + /// + /// + public static BotDeviceInfo UpdateDeviceInfo(this BotContext bot) + => bot.ContextCollection.Device; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Interface/Api/GroupExt.cs b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/GroupExt.cs new file mode 100644 index 0000000..470bb46 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/GroupExt.cs @@ -0,0 +1,151 @@ +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Event.EventArg; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; + +namespace Lagrange.Core.Common.Interface.Api; + +public static class GroupExt +{ + /// + /// Mute the member in the group, Bot must be admin + /// + /// target BotContext + /// The uin for target group + /// The uin for target member in such group + /// The duration in seconds, 0 for unmute member + /// Successfully muted or not + public static Task MuteGroupMember(this BotContext bot, uint groupUin, uint targetUin, uint duration) + => bot.ContextCollection.Business.OperationLogic.MuteGroupMember(groupUin, targetUin, duration); + + /// + /// Mute the group + /// + /// target BotContext + /// The uin for target group + /// true for mute and false for unmute + /// Successfully muted or not + public static Task MuteGroupGlobal(this BotContext bot, uint groupUin, bool isMute) + => bot.ContextCollection.Business.OperationLogic.MuteGroupGlobal(groupUin, isMute); + + /// + /// + /// + /// target BotContext + /// The uin for target group + /// The uin for target member in such group + /// whether the kicked member can request + /// Successfully kicked or not + public static Task KickGroupMember(this BotContext bot, uint groupUin, uint targetUin, bool rejectAddRequest) + => bot.ContextCollection.Business.OperationLogic.KickGroupMember(groupUin, targetUin, rejectAddRequest, ""); + + public static Task KickGroupMember(this BotContext bot, uint groupUin, uint targetUin, bool rejectAddRequest, string reason) + => bot.ContextCollection.Business.OperationLogic.KickGroupMember(groupUin, targetUin, rejectAddRequest, reason); + + public static Task SetGroupAdmin(this BotContext bot, uint groupUin, uint targetUin, bool isAdmin) + => bot.ContextCollection.Business.OperationLogic.SetGroupAdmin(groupUin, targetUin, isAdmin); + + // 300204 Check group manager:Not an administrator + public static Task<(int, string?)> SetGroupTodo(this BotContext bot, uint groupUin, uint sequence) + => bot.ContextCollection.Business.OperationLogic.SetGroupTodo(groupUin, sequence); + + public static Task<(int, string?)> RemoveGroupTodo(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.RemoveGroupTodo(groupUin); + + public static Task<(int, string?)> FinishGroupTodo(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.FinishGroupTodo(groupUin); + + public static Task GetGroupTodo(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.GetGroupTodo(groupUin); + + public static Task SetGroupBot(this BotContext bot, uint targetUin, uint On, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.SetGroupBot(targetUin, On, groupUin); + + [Obsolete("Cosider using SetGroupBotHD(BotContext, uint, uint, string?, string?) instead")] + public static Task SetGroupBotHD(this BotContext bot, uint targetUin, uint groupUin) + => bot.SetGroupBotHD(targetUin, groupUin, null, null); + public static Task SetGroupBotHD(this BotContext bot, uint targetUin, uint groupUin, string? data_1, string? data_2) + => bot.ContextCollection.Business.OperationLogic.SetGroupBotHD(targetUin, groupUin, data_1, data_2); + + public static Task RenameGroupMember(this BotContext bot, uint groupUin, uint targetUin, string targetName) + => bot.ContextCollection.Business.OperationLogic.RenameGroupMember(groupUin, targetUin, targetName); + + public static Task RenameGroup(this BotContext bot, uint groupUin, string targetName) + => bot.ContextCollection.Business.OperationLogic.RenameGroup(groupUin, targetName); + + public static Task RemarkGroup(this BotContext bot, uint groupUin, string targetRemark) + => bot.ContextCollection.Business.OperationLogic.RemarkGroup(groupUin, targetRemark); + + public static Task LeaveGroup(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.LeaveGroup(groupUin); + + public static Task InviteGroup(this BotContext bot, uint groupUin, Dictionary invitedUins) + => bot.ContextCollection.Business.OperationLogic.InviteGroup(groupUin, invitedUins); + + public static Task SetGroupRequest(this BotContext bot, BotGroupRequest request, bool accept = true, string reason = "") + => bot.ContextCollection.Business.OperationLogic.SetGroupRequest(request.GroupUin, request.Sequence, (uint)request.EventType, accept, reason); + + public static Task SetGroupFilteredRequest(this BotContext bot, BotGroupRequest request, bool accept = true, string reason = "") + => bot.ContextCollection.Business.OperationLogic.SetGroupFilteredRequest(request.GroupUin, request.Sequence, (uint)request.EventType, accept, reason); + + public static Task SetFriendRequest(this BotContext bot, BotFriendRequest request, bool accept = true) + => bot.ContextCollection.Business.OperationLogic.SetFriendRequest(request.SourceUid, accept); + + public static Task GroupPoke(this BotContext bot, uint groupUin, uint friendUin) + => bot.ContextCollection.Business.OperationLogic.GroupPoke(groupUin, friendUin); + + public static Task SetEssenceMessage(this BotContext bot, MessageChain chain) + => bot.ContextCollection.Business.OperationLogic.SetEssenceMessage(chain.GroupUin ?? 0, chain.Sequence, (uint)(chain.MessageId & 0xFFFFFFFF)); + + public static Task RemoveEssenceMessage(this BotContext bot, MessageChain chain) + => bot.ContextCollection.Business.OperationLogic.RemoveEssenceMessage(chain.GroupUin ?? 0, chain.Sequence, (uint)(chain.MessageId & 0xFFFFFFFF)); + + public static Task GroupSetSpecialTitle(this BotContext bot, uint groupUin, uint targetUin, string title) + => bot.ContextCollection.Business.OperationLogic.GroupSetSpecialTitle(groupUin, targetUin, title); + + public static Task GroupSetMessageReaction(this BotContext bot, uint groupUin, uint sequence, string code) + => bot.ContextCollection.Business.OperationLogic.SetMessageReaction(groupUin, sequence, code, true); + + public static Task GroupSetMessageReaction(this BotContext bot, uint groupUin, uint sequence, string code, bool isSet) + => bot.ContextCollection.Business.OperationLogic.SetMessageReaction(groupUin, sequence, code, isSet); + + public static Task GroupSetAvatar(this BotContext bot, uint groupUin, ImageEntity imageEntity) + => bot.ContextCollection.Business.OperationLogic.GroupSetAvatar(groupUin, imageEntity); + + public static Task<(uint, uint)> GroupRemainAtAll(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.GroupRemainAtAll(groupUin); + + #region Group File System + + public static Task FetchGroupFSSpace(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.FetchGroupFSSpace(groupUin); + + public static Task FetchGroupFSCount(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.FetchGroupFSCount(groupUin); + + public static Task> FetchGroupFSList(this BotContext bot, uint groupUin, string targetDirectory = "/") + => bot.ContextCollection.Business.OperationLogic.FetchGroupFSList(groupUin, targetDirectory); + + public static Task FetchGroupFSDownload(this BotContext bot, uint groupUin, string fileId) + => bot.ContextCollection.Business.OperationLogic.FetchGroupFSDownload(groupUin, fileId); + + public static Task<(int RetCode, string RetMsg)> GroupFSMove(this BotContext bot, uint groupUin, string fileId, string parentDirectory, string targetDirectory) + => bot.ContextCollection.Business.OperationLogic.GroupFSMove(groupUin, fileId, parentDirectory, targetDirectory); + + public static Task<(int RetCode, string RetMsg)> GroupFSDelete(this BotContext bot, uint groupUin, string fileId) + => bot.ContextCollection.Business.OperationLogic.GroupFSDelete(groupUin, fileId); + + public static Task<(int RetCode, string RetMsg)> GroupFSCreateFolder(this BotContext bot, uint groupUin, string name) + => bot.ContextCollection.Business.OperationLogic.GroupFSCreateFolder(groupUin, name); + + public static Task<(int RetCode, string RetMsg)> GroupFSDeleteFolder(this BotContext bot, uint groupUin, string folderId) + => bot.ContextCollection.Business.OperationLogic.GroupFSDeleteFolder(groupUin, folderId); + + public static Task<(int RetCode, string RetMsg)> GroupFSRenameFolder(this BotContext bot, uint groupUin, string folderId, string newFolderName) + => bot.ContextCollection.Business.OperationLogic.GroupFSRenameFolder(groupUin, folderId, newFolderName); + + public static Task GroupFSUpload(this BotContext bot, uint groupUin, FileEntity fileEntity, string targetDirectory = "/") + => bot.ContextCollection.Business.OperationLogic.GroupFSUpload(groupUin, fileEntity, targetDirectory); + + #endregion +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Interface/Api/OperationExt.cs b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/OperationExt.cs new file mode 100644 index 0000000..626f4e9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Interface/Api/OperationExt.cs @@ -0,0 +1,302 @@ +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; + +namespace Lagrange.Core.Common.Interface.Api; + +public static class OperationExt +{ + /// + /// Fetch the friend list of account from server or cache + /// + /// target BotContext + /// force the cache to be refreshed + /// + public static Task> FetchFriends(this BotContext bot, bool refreshCache = false) + => bot.ContextCollection.Business.OperationLogic.FetchFriends(refreshCache); + + /// + /// Fetch the member list of the group from server or cache + /// + /// target BotContext + /// + /// force the cache to be refreshed + /// + public static Task> FetchMembers(this BotContext bot, uint groupUin, bool refreshCache = false) + => bot.ContextCollection.Business.OperationLogic.FetchMembers(groupUin, refreshCache); + + /// + /// Fetch the group list of the account from server or cache + /// + /// target BotContext + /// force the cache to be refreshed + /// + public static Task> FetchGroups(this BotContext bot, bool refreshCache = false) + => bot.ContextCollection.Business.OperationLogic.FetchGroups(refreshCache); + + /// + /// Fetch the cookies/pskey for accessing other site + /// + /// target BotContext + /// the domain for the cookie to be valid + /// the list of cookies + public static Task> FetchCookies(this BotContext bot, List domains) + => bot.ContextCollection.Business.OperationLogic.GetCookies(domains); + + /// + /// Send the message + /// + /// target BotContext + /// the chain constructed by + public static Task SendMessage(this BotContext bot, MessageChain chain) + => bot.ContextCollection.Business.OperationLogic.SendMessage(chain); + + /// + /// Recall the group message from Bot itself by + /// + /// target BotContext + /// The uin for target group of the message + /// The return value for + /// Successfully recalled or not + public static Task RecallGroupMessage(this BotContext bot, uint groupUin, MessageResult result) + => bot.ContextCollection.Business.OperationLogic.RecallGroupMessage(groupUin, result); + + /// + /// Recall the group message by + /// + /// target BotContext + /// target MessageChain, must be Group + /// Successfully recalled or not + public static Task RecallGroupMessage(this BotContext bot, MessageChain chain) + => bot.ContextCollection.Business.OperationLogic.RecallGroupMessage(chain); + + /// + /// Recall the group message by sequence + /// + /// target BotContext + /// The uin for target group of the message + /// The sequence for target message + /// Successfully recalled or not + public static Task RecallGroupMessage(this BotContext bot, uint groupUin, uint sequence) + => bot.ContextCollection.Business.OperationLogic.RecallGroupMessage(groupUin, sequence); + + /// + /// Recall the group message from Bot itself by + /// + /// target BotContext + /// The uin for target friend of the message + /// The return value for + /// Successfully recalled or not + public static Task RecallFriendMessage(this BotContext bot, uint friendUin, MessageResult result) + => bot.ContextCollection.Business.OperationLogic.RecallFriendMessage(friendUin, result); + + /// + /// Recall the group message by + /// + /// target BotContext + /// target MessageChain, must be Friend + /// Successfully recalled or not + public static Task RecallFriendMessage(this BotContext bot, MessageChain chain) + => bot.ContextCollection.Business.OperationLogic.RecallFriendMessage(chain); + + /// + /// Fetch Notifications and requests such as friend requests and Group Join Requests + /// + /// target BotContext + /// + public static Task?> FetchGroupRequests(this BotContext bot) + => bot.ContextCollection.Business.OperationLogic.FetchGroupRequests(); + + /// + /// + /// + /// + /// + public static Task?> FetchFriendRequests(this BotContext bot) + => bot.ContextCollection.Business.OperationLogic.FetchFriendRequests(); + + /// + /// set status + /// + /// target BotContext + /// The status code + /// + public static Task SetStatus(this BotContext bot, uint status) + => bot.ContextCollection.Business.OperationLogic.SetStatus(status); + + /// + /// set custom status + /// + /// target BotContext + /// faceId that is same as the + /// text that would shown + /// + public static Task SetCustomStatus(this BotContext bot, uint faceId, string text) + => bot.ContextCollection.Business.OperationLogic.SetCustomStatus(faceId, text); + + public static Task GroupTransfer(this BotContext bot, uint groupUin, uint targetUin) + => bot.ContextCollection.Business.OperationLogic.GroupTransfer(groupUin, targetUin); + + public static Task DeleteFriend(this BotContext bot, uint friendUin, bool block) + => bot.ContextCollection.Business.OperationLogic.DeleteFriend(friendUin, block); + + public static Task RequestFriend(this BotContext bot, uint targetUin, string question = "", string message = "") + => bot.ContextCollection.Business.OperationLogic.RequestFriend(targetUin, question, message); + + public static Task Like(this BotContext bot, uint targetUin, uint count = 1) + => bot.ContextCollection.Business.OperationLogic.Like(targetUin, count); + + /// + /// Get the client key for all sites + /// + public static Task GetClientKey(this BotContext bot) + => bot.ContextCollection.Business.OperationLogic.GetClientKey(); + + /// + /// Get the history message record, max 30 seqs + /// + /// target BotContext + /// target GroupUin + /// Start Sequence of the message + /// End Sequence of the message + public static Task?> GetGroupMessage(this BotContext bot, uint groupUin, uint startSequence, uint endSequence) + => bot.ContextCollection.Business.OperationLogic.GetGroupMessage(groupUin, startSequence, endSequence); + + /// + /// Get the history message record for private message + /// + /// target BotContext + /// target FriendUin + /// timestamp of the message chain + /// number of message to be fetched before timestamp + public static Task?> GetRoamMessage(this BotContext bot, uint friendUin, uint timestamp, uint count) + => bot.ContextCollection.Business.OperationLogic.GetRoamMessage(friendUin, timestamp, count); + + /// + /// Get the history message record for private message + /// + /// target BotContext + /// target chain + /// number of message to be fetched before timestamp + public static Task?> GetRoamMessage(this BotContext bot, MessageChain targetChain, uint count) + { + uint timestamp = (uint)new DateTimeOffset(targetChain.Time).ToUnixTimeSeconds(); + return bot.ContextCollection.Business.OperationLogic.GetRoamMessage(targetChain.FriendUin, timestamp, count); + } + + public static Task?> GetC2cMessage(this BotContext bot, uint friendUin, uint startSequence, uint endSequence) + { + return bot.ContextCollection.Business.OperationLogic.GetC2cMessage(friendUin, startSequence, endSequence); + } + + public static Task<(int code, List? chains)> GetMessagesByResId(this BotContext bot, string resId) + { + return bot.ContextCollection.Business.OperationLogic.GetMessagesByResId(resId); + } + + /// + /// Do group clock in (群打卡) + /// + /// target BotContext + /// target groupUin + /// + public static Task GroupClockIn(this BotContext bot, uint groupUin) + => bot.ContextCollection.Business.OperationLogic.GroupClockIn(groupUin); + + public static Task FetchUserInfo(this BotContext bot, uint uin, bool refreshCache = false) + => bot.ContextCollection.Business.OperationLogic.FetchUserInfo(uin, refreshCache); + + public static Task<(int code, string? message, BotGroupInfo info)> FetchGroupInfo(this BotContext bot, ulong uin) + => bot.ContextCollection.Business.OperationLogic.FetchGroupInfo(uin); + + public static Task?> FetchCustomFace(this BotContext bot) + => bot.ContextCollection.Business.OperationLogic.FetchCustomFace(); + + public static Task UploadLongMessage(this BotContext bot, List chains) + => bot.ContextCollection.Business.OperationLogic.UploadLongMessage(chains); + + public static Task MarkAsRead(this BotContext bot, MessageChain targetChain) + { + uint timestamp = (uint)(targetChain.Time - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; + return bot.ContextCollection.Business.OperationLogic.MarkAsRead(targetChain.GroupUin ?? 0, targetChain.Uid, + targetChain.Sequence, timestamp); + } + + public static Task UploadFriendFile(this BotContext bot, uint targetUin, FileEntity fileEntity) + => bot.ContextCollection.Business.OperationLogic.UploadFriendFile(targetUin, fileEntity); + + public static Task FriendPoke(this BotContext bot, uint friendUin) + => bot.ContextCollection.Business.OperationLogic.FriendPoke(friendUin); + + /// + /// Send a special window shake to friend + /// + /// target friend uin + /// face type + /// count of face + public static Task FriendSpecialShake(this BotContext bot, uint friendUin, SpecialPokeFaceType type, uint count) + => bot.ContextCollection.Business.OperationLogic.FriendSpecialShake(friendUin, type, count); + + /// + /// Send a window shake to friend + /// + /// target friend uin + /// face type + /// How big the face will be displayed ([0,3] is valid) + public static Task FriendShake(this BotContext bot, uint friendUin, PokeFaceType type, ushort strength) + => bot.ContextCollection.Business.OperationLogic.FriendShake(friendUin, type, strength); + + public static Task?> FetchMarketFaceKey(this BotContext bot, List faceIds) + => bot.ContextCollection.Business.OperationLogic.FetchMarketFaceKey(faceIds); + + /// + /// Set the avatar of the bot itself + /// + /// target + /// The avatar object, + public static Task SetAvatar(this BotContext bot, ImageEntity avatar) + => bot.ContextCollection.Business.OperationLogic.SetAvatar(avatar); + + public static Task FetchSuperFaceId(this BotContext bot, uint id) + => bot.ContextCollection.Business.OperationLogic.FetchSuperFaceId(id); + + public static Task FetchFaceEntity(this BotContext bot, uint id) + => bot.ContextCollection.Business.OperationLogic.FetchFaceEntity(id); + + public static Task GroupJoinEmojiChain(this BotContext bot, uint groupUin, uint emojiId, uint targetMessageSeq) + => bot.ContextCollection.Business.OperationLogic.GroupJoinEmojiChain(groupUin, emojiId, targetMessageSeq); + + public static Task FriendJoinEmojiChain(this BotContext bot, uint friendUin, uint emojiId, + uint targetMessageSeq) + => bot.ContextCollection.Business.OperationLogic.FriendJoinEmojiChain(friendUin, emojiId, targetMessageSeq); + + public static Task UploadImage(this BotContext bot, ImageEntity entity) + => bot.ContextCollection.Business.OperationLogic.UploadImage(entity); + + public static Task OcrImage(this BotContext bot, string url) + => bot.ContextCollection.Business.OperationLogic.ImageOcr(url); + + public static Task OcrImage(this BotContext bot, ImageEntity entity) + => bot.ContextCollection.Business.OperationLogic.ImageOcr(entity); + + public static Task<(int Code, string ErrMsg, string? Url)> GetGroupGenerateAiRecordUrl(this BotContext bot, uint groupUin, string character, string text, uint chatType) + => bot.ContextCollection.Business.OperationLogic.GetGroupGenerateAiRecordUrl(groupUin, character, text, chatType); + + public static Task<(int Code, string ErrMsg, RecordEntity? RecordEntity)> GetGroupGenerateAiRecord(this BotContext bot, uint groupUin, string character, string text, uint chatType) + => bot.ContextCollection.Business.OperationLogic.GetGroupGenerateAiRecord(groupUin, character, text, chatType); + + public static Task<(int Code, string ErrMsg, List? Result)> GetAiCharacters(this BotContext bot, uint chatType, uint groupUin = 42) + => bot.ContextCollection.Business.OperationLogic.GetAiCharacters(chatType, groupUin); + + public static Task<(int Retcode, string Message, List FriendUins, List GroupUins)> GetPins(this BotContext bot) + => bot.ContextCollection.Business.OperationLogic.GetPins(); + + public static Task<(int Retcode, string Message)> SetPinFriend(this BotContext bot, uint uin, bool isPin) + => bot.ContextCollection.Business.OperationLogic.SetPinFriend(uin, isPin); + + public static Task<(int Retcode, string Message)> SetPinGroup(this BotContext bot, uint uin, bool isPin) + => bot.ContextCollection.Business.OperationLogic.SetPinGroup(uin, isPin); + + public static Task FetchPrivateFSDownload(this BotContext bot, string fileId, string fileHash, uint userId) + => bot.ContextCollection.Business.OperationLogic.FetchPrivateFSDownload(fileId, fileHash, userId); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Common/Interface/BotFactory.cs b/Lagrange.Core/Lagrange.Core/Common/Interface/BotFactory.cs new file mode 100644 index 0000000..fe1a92b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Common/Interface/BotFactory.cs @@ -0,0 +1,48 @@ +namespace Lagrange.Core.Common.Interface; + +public static class BotFactory +{ + /// + /// Create new Bot from existing and + /// + /// The config for Bot + /// Existing DeviceInfo from deserialization + /// Existing Keystore from deserialization + /// Created BotContext Instance + public static BotContext Create(BotConfig config, BotDeviceInfo deviceInfo, BotKeystore keystore) => + new(config, deviceInfo, keystore, BotAppInfo.ProtocolToAppInfo[config.Protocol]); + + /// + /// Create new Bot from existing and with custom + /// + /// The config for Bot + /// Existing DeviceInfo from deserialization + /// Existing Keystore from deserialization + /// Custom BotAppInfo, including client version, app ID, etc + /// + public static BotContext Create(BotConfig config, BotDeviceInfo deviceInfo, BotKeystore keystore, BotAppInfo appInfo) => + new(config, deviceInfo, keystore, appInfo); + + /// + /// Create new Bot from Password and uin + /// + /// The config for Bot + /// Uin, if QrCode login is used, ensure the account that scans QrCode is consistent with this Uin + /// The password of account, for Password Login + /// Created device, should be serialized to files for next use + /// Created BotContext Instance + public static BotContext Create(BotConfig config, uint uin, string password, out BotDeviceInfo device) => + new(config, device = BotDeviceInfo.GenerateInfo(), new BotKeystore(uin, password), BotAppInfo.ProtocolToAppInfo[config.Protocol]); + + /// + /// Create new Bot from Password and uin with custom + /// + /// The config for Bot + /// Uin, if QrCode login is used, ensure the account that scans QrCode is consistent with this Uin + /// The password of account, for Password Login + /// Custom BotAppInfo, including client version, app ID, etc + /// Created device, should be serialized to files for next use + /// + public static BotContext Create(BotConfig config, uint uin, string password, BotAppInfo appInfo, out BotDeviceInfo device) => + new(config, device = BotDeviceInfo.GenerateInfo(), new BotKeystore(uin, password), appInfo); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/BotCaptchaEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotCaptchaEvent.cs new file mode 100644 index 0000000..8928453 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotCaptchaEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Event.EventArg; + +public class BotCaptchaEvent : EventBase +{ + public string Url { get; } + + public BotCaptchaEvent(string url) + { + Url = url; + EventMessage = $"[{nameof(BotCaptchaEvent)}]: Url: {url}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/BotLogEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotLogEvent.cs new file mode 100644 index 0000000..b16b5e4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotLogEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Event.EventArg; + +public enum LogLevel +{ + Debug, + Verbose, + Information, + Warning, + Exception, + Fatal +} + +public class BotLogEvent : EventBase +{ + private const string DateFormat = "yyyy-MM-dd HH:mm:ss"; + + public string Tag { get; } + + public LogLevel Level { get; } + + internal BotLogEvent(string tag, LogLevel level, string content) + { + Tag = tag; + Level = level; + EventMessage = content; + } + + public override string ToString() => + $"[{EventTime.ToString(DateFormat)}] [{Tag}] [{Level.ToString().ToUpper()}]: {EventMessage}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/BotNewDeviceVerifyEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotNewDeviceVerifyEvent.cs new file mode 100644 index 0000000..d5dda00 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotNewDeviceVerifyEvent.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Event.EventArg; + +public class BotNewDeviceVerifyEvent : EventBase +{ + public string Url { get; } + + public byte[] QrCode { get; } + + public BotNewDeviceVerifyEvent(string url, byte[] qrCode) + { + Url = url; + EventMessage = $"[{nameof(BotNewDeviceVerifyEvent)}]: Url: {url}"; + QrCode = qrCode; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOfflineEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOfflineEvent.cs new file mode 100644 index 0000000..46673b0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOfflineEvent.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Event.EventArg; + +public class BotOfflineEvent : EventBase +{ + public string Tag { get; } + + public string Message { get; } + + public BotOfflineEvent(string tag, string message) + { + Tag = tag; + Message = message; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOnlineEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOnlineEvent.cs new file mode 100644 index 0000000..22ede1b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/BotOnlineEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Event.EventArg; + +public class BotOnlineEvent : EventBase +{ + public OnlineReason Reason { get; } + + public BotOnlineEvent(OnlineReason reason) + { + Reason = reason; + EventMessage = $"[{nameof(BotOnlineEvent)}]: {Reason}"; + } + + public enum OnlineReason + { + Login, + Reconnect + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/DeviceLoginEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/DeviceLoginEvent.cs new file mode 100644 index 0000000..08a04d9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/DeviceLoginEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Event.EventArg; + +public class DeviceLoginEvent : EventBase +{ + public bool IsLogin { get; } + + public uint AppId { get; } + + public string Tag { get; } + + public string Message { get; } + + + public DeviceLoginEvent(bool isLogin, uint appId, string tag, string message) + { + IsLogin = isLogin; + AppId = appId; + Tag = tag; + Message = message; + + EventMessage = $"[{nameof(DeviceLoginEvent)}]: {Tag} | {Message}, AppID: {AppId}, IsLogin: {IsLogin}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendMessageEvent.cs new file mode 100644 index 0000000..ce2fa23 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendMessageEvent.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Event.EventArg; + +public class FriendMessageEvent : EventBase +{ + public MessageChain Chain { get; set; } + + public FriendMessageEvent(MessageChain chain) + { + Chain = chain; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendPokeEvent.cs new file mode 100644 index 0000000..54f0f72 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendPokeEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Event.EventArg; + +public class FriendPokeEvent : EventBase +{ + public uint OperatorUin { get; } + + public uint TargetUin { get; } + + public string Action { get; } + + public string Suffix { get; } + + public string ActionImgUrl { get; } + + public FriendPokeEvent(uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) + { + OperatorUin = operatorUin; + TargetUin = targetUin; + Action = action; + Suffix = suffix; + ActionImgUrl = actionImgUrl; + + EventMessage = $"{nameof(FriendPokeEvent)}: OperatorUin: {OperatorUin} | TargetUin: {TargetUin} | Action: {Action} | Suffix: {Suffix} | ActionImgUrl: {ActionImgUrl}"; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRecallEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRecallEvent.cs new file mode 100644 index 0000000..deef803 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRecallEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Event.EventArg; + +public class FriendRecallEvent : EventBase +{ + public uint FriendUin { get; } + + public uint ClientSequence { get; } + + public uint Time { get; } + + public uint Random { get; } + + public string Tip { get; } + + public FriendRecallEvent(uint friendUin, uint clientSequence, uint time, uint random, string tip) + { + FriendUin = friendUin; + ClientSequence = clientSequence; + Time = time; + Random = random; + Tip = tip; + + EventMessage = $"{nameof(FriendRecallEvent)}: {FriendUin} | ({ClientSequence} | {Time} | {Random} | {Tip})"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRequestEvent.cs new file mode 100644 index 0000000..eb0dd65 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/FriendRequestEvent.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Event.EventArg; + +public class FriendRequestEvent : EventBase +{ + public uint SourceUin { get; } + + internal string SourceUid { get; } + + public string Source { get; } + + public string Message { get; } + + internal FriendRequestEvent(uint sourceUin, string sourceUid, string message, string source) + { + SourceUin = sourceUin; + SourceUid = sourceUid; + Message = message; + Source = source; + + EventMessage = $"[{nameof(FriendRequestEvent)}]: {SourceUin}:{Source} {Message}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupAdminChangedEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupAdminChangedEvent.cs new file mode 100644 index 0000000..23f53b7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupAdminChangedEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupAdminChangedEvent : EventBase +{ + public uint GroupUin { get; } + + public uint AdminUin { get; } + + public bool IsPromote { get; } + + public GroupAdminChangedEvent(uint groupUin, uint adminUin, bool isPromote) + { + GroupUin = groupUin; + AdminUin = adminUin; + IsPromote = isPromote; + EventMessage = $"{nameof(GroupAdminChangedEvent)} | {GroupUin} | {AdminUin} | {IsPromote}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupEssenceEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupEssenceEvent.cs new file mode 100644 index 0000000..c3bf28b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupEssenceEvent.cs @@ -0,0 +1,28 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupEssenceEvent : EventBase +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public uint Random { get; } + + public bool IsSet { get; } // 1 设置精华消息, 2 移除精华消息 + + public uint FromUin { get; } + + public uint OperatorUin { get; } + + public GroupEssenceEvent(uint groupUin, uint sequence,uint random, uint setFlag, uint fromUin, uint operatorUin) + { + GroupUin = groupUin; + Sequence = sequence; + Random = random; + IsSet = setFlag == 1; + FromUin = fromUin; + OperatorUin = operatorUin; + + EventMessage = $"{nameof(GroupEssenceEvent)}: {GroupUin} | {FromUin} | {OperatorUin} | ({Sequence}) | IsSet: {IsSet}"; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationEvent.cs new file mode 100644 index 0000000..9f823ab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationEvent.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupInvitationEvent : EventBase +{ + public uint GroupUin { get; } + + public uint InvitorUin { get; } + + internal GroupInvitationEvent(uint groupUin, uint invitorUin) + { + GroupUin = groupUin; + InvitorUin = invitorUin; + EventMessage = $"[{nameof(GroupInvitationEvent)}]: {GroupUin} from {InvitorUin}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationRequestEvent.cs new file mode 100644 index 0000000..2d2feec --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupInvitationRequestEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupInvitationRequestEvent : EventBase +{ + internal GroupInvitationRequestEvent(uint groupUin, uint targetUin, uint invitorUin) + { + GroupUin = groupUin; + TargetUin = targetUin; + InvitorUin = invitorUin; + EventMessage = $"[{nameof(GroupInvitationRequestEvent)}] {TargetUin} from {InvitorUin} at {GroupUin}"; + } + + public uint GroupUin { get; } + + public uint TargetUin { get; } + + public uint InvitorUin { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupJoinRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupJoinRequestEvent.cs new file mode 100644 index 0000000..bf25eed --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupJoinRequestEvent.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupJoinRequestEvent : EventBase +{ + internal GroupJoinRequestEvent(uint groupUin, uint targetUin) + { + TargetUin = targetUin; + GroupUin = groupUin; + EventMessage = $"[{nameof(GroupJoinRequestEvent)}] {TargetUin} at {GroupUin}"; + } + + public uint TargetUin { get; } + + public uint GroupUin { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberDecreaseEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberDecreaseEvent.cs new file mode 100644 index 0000000..f4ab406 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberDecreaseEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMemberDecreaseEvent : EventBase +{ + public uint GroupUin { get; } + + public uint MemberUin { get; } + + public uint? OperatorUin { get; } + + public EventType Type { get; } + + public GroupMemberDecreaseEvent(uint groupUin, uint memberUin, uint? operatorUin, uint type) + { + GroupUin = groupUin; + MemberUin = memberUin; + OperatorUin = operatorUin; + Type = (EventType)type; + + EventMessage = $"{nameof(GroupMemberDecreaseEvent)}: {GroupUin} | {MemberUin} | {OperatorUin} | {Type}"; + } + + public enum EventType : uint + { + KickMe = 3, + Disband = 129, + Leave = 130, + Kick = 131 + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs new file mode 100644 index 0000000..95808da --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMemberEnterEvent : EventBase +{ + public uint GroupUin { get; } + + public uint GroupMemberUin { get; } + + public uint StyleId { get; } + + internal GroupMemberEnterEvent(uint groupUin, uint groupMemberUin, uint styleId) + { + GroupUin = groupUin; + GroupMemberUin = groupMemberUin; + StyleId = styleId; + + EventMessage = $"[{nameof(GroupMemberEnterEvent)}]: {GroupMemberUin} Enter {GroupUin} | {StyleId}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberIncreaseEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberIncreaseEvent.cs new file mode 100644 index 0000000..1c64539 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberIncreaseEvent.cs @@ -0,0 +1,28 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMemberIncreaseEvent : EventBase +{ + public uint GroupUin { get; } + + public uint MemberUin { get; } + + public uint? InvitorUin { get; } + + public EventType Type { get; } + + public GroupMemberIncreaseEvent(uint groupUin, uint memberUin, uint? invitorUin, uint type) + { + GroupUin = groupUin; + MemberUin = memberUin; + InvitorUin = invitorUin; + Type = (EventType)type; + + EventMessage = $"{nameof(GroupMemberIncreaseEvent)}: {GroupUin} | {MemberUin} | {InvitorUin} | {Type}"; + } + + public enum EventType : uint + { + Approve = 130, + Invite = 131 + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberMuteEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberMuteEvent.cs new file mode 100644 index 0000000..74adc66 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMemberMuteEvent.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMemberMuteEvent : EventBase +{ + public uint GroupUin { get; } + + public uint TargetUin { get; } + + public uint? OperatorUin { get; } + + public uint Duration { get; } + + public GroupMemberMuteEvent(uint groupUin, uint targetUin, uint? operatorUin, uint duration) + { + GroupUin = groupUin; + TargetUin = targetUin; + OperatorUin = operatorUin; + Duration = duration; + + EventMessage = $"{nameof(GroupMemberMuteEvent)}: {GroupUin} | {TargetUin} | {OperatorUin} | {Duration}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMessageEvent.cs new file mode 100644 index 0000000..b236e53 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMessageEvent.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Event.EventArg; + +public class GroupMessageEvent : EventBase +{ + public MessageChain Chain { get; set; } + + public GroupMessageEvent(MessageChain chain) + { + Chain = chain; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMuteEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMuteEvent.cs new file mode 100644 index 0000000..1e3fe62 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupMuteEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMuteEvent : EventBase +{ + public uint GroupUin { get; } + + public uint? OperatorUin { get; } + + public bool IsMuted { get; } + + public GroupMuteEvent(uint groupUin, uint? operatorUin, bool isMuted) + { + GroupUin = groupUin; + OperatorUin = operatorUin; + IsMuted = isMuted; + + EventMessage = $"{nameof(GroupMuteEvent)}: {GroupUin} | {OperatorUin} | IsMuted: {IsMuted}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupNameChangeEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupNameChangeEvent.cs new file mode 100644 index 0000000..fbc2522 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupNameChangeEvent.cs @@ -0,0 +1,16 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupNameChangeEvent : EventBase +{ + public uint GroupUin { get; } + + public string Name { get; } + + public GroupNameChangeEvent(uint groupUin, string name) + { + GroupUin = groupUin; + Name = name; + + EventMessage = $"{nameof(GroupNameChangeEvent)}: GroupUin: {GroupUin} | Name: {Name}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupPokeEvent.cs new file mode 100644 index 0000000..ea41cc1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupPokeEvent.cs @@ -0,0 +1,28 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupPokeEvent : EventBase +{ + public uint GroupUin { get; } + + public uint OperatorUin { get; } + + public uint TargetUin { get; } + + public string Action { get; } + + public string Suffix { get; } + + public string ActionImgUrl { get; } + + public GroupPokeEvent(uint groupUin, uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) + { + GroupUin = groupUin; + OperatorUin = operatorUin; + TargetUin = targetUin; + Action = action; + Suffix = suffix; + ActionImgUrl = actionImgUrl; + + EventMessage = $"{nameof(GroupPokeEvent)}: GroupUin: {GroupUin} | OperatorUin: {OperatorUin} | TargetUin: {TargetUin} | Action: {Action} | Suffix: {Suffix} | ActionImgUrl: {ActionImgUrl}"; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupReactionEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupReactionEvent.cs new file mode 100644 index 0000000..e72995c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupReactionEvent.cs @@ -0,0 +1,28 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupReactionEvent : EventBase +{ + public uint TargetGroupUin { get; } + + public uint TargetSequence { get; } + + public uint OperatorUin { get; } + + public bool IsAdd { get; } + + public string Code { get; } + + public uint Count { get; } + + public GroupReactionEvent(uint targetGroupUin, uint targetSequence, uint operatorUin, bool isAdd, string code, uint count) + { + TargetGroupUin = targetGroupUin; + TargetSequence = targetSequence; + OperatorUin = operatorUin; + IsAdd = isAdd; + Code = code; + Count = count; + + EventMessage = $"{nameof(GroupReactionEvent)}: TargetGroupUin: {TargetGroupUin} | TargetSequence: {TargetSequence} | OperatorUin: {OperatorUin} | IsAdd: {IsAdd} | Code: {Code} | Count: {Count}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupRecallEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupRecallEvent.cs new file mode 100644 index 0000000..401521a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupRecallEvent.cs @@ -0,0 +1,31 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupRecallEvent : EventBase +{ + public uint GroupUin { get; } + + public uint AuthorUin { get; } + + public uint OperatorUin { get; } + + public uint Sequence { get; } + + public uint Time { get; } + + public uint Random { get; } + + public string Tip { get; } + + public GroupRecallEvent(uint groupUin, uint authorUin, uint operatorUin, uint sequence, uint time, uint random, string tip) + { + GroupUin = groupUin; + AuthorUin = authorUin; + OperatorUin = operatorUin; + Sequence = sequence; + Time = time; + Random = random; + Tip = tip; + + EventMessage = $"{nameof(GroupRecallEvent)}: {GroupUin} | {AuthorUin} | {OperatorUin} | ({Sequence} | {Time} | {Random} | {Tip})"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupTodoEvent.cs new file mode 100644 index 0000000..b473b40 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/GroupTodoEvent.cs @@ -0,0 +1,16 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupTodoEvent : EventBase +{ + public uint GroupUin { get; } + + public uint OperatorUin { get; } + + public GroupTodoEvent(uint groupUin, uint operatorUin) + { + GroupUin = groupUin; + OperatorUin = operatorUin; + + EventMessage = $"{nameof(GroupPokeEvent)}: GroupUin: {GroupUin} | OperatorUin: {OperatorUin}"; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/PinChangedEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/PinChangedEvent.cs new file mode 100644 index 0000000..dd45b3d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/PinChangedEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Event.EventArg; + +public class PinChangedEvent : EventBase +{ + public ChatType Type { get; } + + public uint Uin { get; } + + public bool IsPin { get; } + + public PinChangedEvent(ChatType type, uint uin, bool isPin) + { + Type = type; + Uin = uin; + IsPin = isPin; + + EventMessage = $"{nameof(PinChangedEvent)} {{ChatType: {Type} | Uin: {Uin} | IsPin: {IsPin}}}"; + } + + public enum ChatType + { + Friend, + Group, + Service + } +} diff --git a/Lagrange.Core/Lagrange.Core/Event/EventArg/TempMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Event/EventArg/TempMessageEvent.cs new file mode 100644 index 0000000..308ddad --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventArg/TempMessageEvent.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Event.EventArg; + +public class TempMessageEvent : EventBase +{ + public MessageChain Chain { get; set; } + + public TempMessageEvent(MessageChain chain) + { + Chain = chain; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventBase.cs b/Lagrange.Core/Lagrange.Core/Event/EventBase.cs new file mode 100644 index 0000000..337e28f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventBase.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Event; + +/// +/// Event that exposed to user +/// +public abstract class EventBase : EventArgs +{ + public DateTime EventTime { get; } + + public string EventMessage { get; protected set; } + + internal EventBase() + { + EventTime = DateTime.Now; + EventMessage = "[Empty Event Message]"; + } + + public override string ToString() + { + return $"[{EventTime:HH:mm:ss}] {EventMessage}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventInvoker.Events.cs b/Lagrange.Core/Lagrange.Core/Event/EventInvoker.Events.cs new file mode 100644 index 0000000..c5725b1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventInvoker.Events.cs @@ -0,0 +1,62 @@ +using Lagrange.Core.Event.EventArg; + +namespace Lagrange.Core.Event; + +public partial class EventInvoker +{ + public event LagrangeEvent? OnBotOnlineEvent; + + public event LagrangeEvent? OnBotOfflineEvent; + + public event LagrangeEvent? OnBotLogEvent; + + public event LagrangeEvent? OnBotCaptchaEvent; + + public event LagrangeEvent? OnBotNewDeviceVerify; + + public event LagrangeEvent? OnGroupInvitationReceived; + + public event LagrangeEvent? OnFriendMessageReceived; + + public event LagrangeEvent? OnGroupMessageReceived; + + public event LagrangeEvent? OnTempMessageReceived; + + public event LagrangeEvent? OnGroupAdminChangedEvent; + + public event LagrangeEvent? OnGroupMemberIncreaseEvent; + + public event LagrangeEvent? OnGroupMemberDecreaseEvent; + + public event LagrangeEvent? OnFriendRequestEvent; + + public event LagrangeEvent? OnGroupInvitationRequestEvent; + + public event LagrangeEvent? OnGroupJoinRequestEvent; + + public event LagrangeEvent? OnGroupMuteEvent; + + public event LagrangeEvent? OnGroupMemberMuteEvent; + + public event LagrangeEvent? OnGroupRecallEvent; + + public event LagrangeEvent? OnFriendRecallEvent; + + public event LagrangeEvent? OnDeviceLoginEvent; + + public event LagrangeEvent? OnFriendPokeEvent; + + public event LagrangeEvent? OnGroupPokeEvent; + + public event LagrangeEvent? OnGroupEssenceEvent; + + public event LagrangeEvent? OnGroupReactionEvent; + + public event LagrangeEvent? OnGroupNameChangeEvent; + + public event LagrangeEvent? OnGroupTodoEvent; + + public event LagrangeEvent? OnGroupMemberEnterEvent; + + public event LagrangeEvent? OnPinChangedEvent; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Event/EventInvoker.cs b/Lagrange.Core/Lagrange.Core/Event/EventInvoker.cs new file mode 100644 index 0000000..925ea82 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Event/EventInvoker.cs @@ -0,0 +1,72 @@ +using System.Runtime.CompilerServices; +using Lagrange.Core.Event.EventArg; + +namespace Lagrange.Core.Event; + +public partial class EventInvoker : IDisposable +{ + private const string Tag = "EventInvoker"; + + private readonly Dictionary> _events; + + public delegate void LagrangeEvent(BotContext context, TEvent e) where TEvent : EventBase; + + internal EventInvoker(BotContext context) + { + _events = new Dictionary>(); + + RegisterEvent((BotOnlineEvent e) => OnBotOnlineEvent?.Invoke(context, e)); + RegisterEvent((BotOfflineEvent e) => OnBotOfflineEvent?.Invoke(context, e)); + RegisterEvent((BotLogEvent e) => OnBotLogEvent?.Invoke(context, e)); + RegisterEvent((BotCaptchaEvent e) => OnBotCaptchaEvent?.Invoke(context, e)); + RegisterEvent((BotNewDeviceVerifyEvent e) => OnBotNewDeviceVerify?.Invoke(context, e)); + RegisterEvent((GroupInvitationEvent e) => OnGroupInvitationReceived?.Invoke(context, e)); + RegisterEvent((FriendMessageEvent e) => OnFriendMessageReceived?.Invoke(context, e)); + RegisterEvent((GroupMessageEvent e) => OnGroupMessageReceived?.Invoke(context, e)); + RegisterEvent((TempMessageEvent e) => OnTempMessageReceived?.Invoke(context, e)); + RegisterEvent((GroupAdminChangedEvent e) => OnGroupAdminChangedEvent?.Invoke(context, e)); + RegisterEvent((GroupMemberIncreaseEvent e) => OnGroupMemberIncreaseEvent?.Invoke(context, e)); + RegisterEvent((GroupMemberDecreaseEvent e) => OnGroupMemberDecreaseEvent?.Invoke(context, e)); + RegisterEvent((FriendRequestEvent e) => OnFriendRequestEvent?.Invoke(context, e)); + RegisterEvent((GroupInvitationRequestEvent e) => OnGroupInvitationRequestEvent?.Invoke(context, e)); + RegisterEvent((GroupJoinRequestEvent e) => OnGroupJoinRequestEvent?.Invoke(context, e)); + RegisterEvent((GroupMuteEvent e) => OnGroupMuteEvent?.Invoke(context, e)); + RegisterEvent((GroupMemberMuteEvent e) => OnGroupMemberMuteEvent?.Invoke(context, e)); + RegisterEvent((GroupRecallEvent e) => OnGroupRecallEvent?.Invoke(context, e)); + RegisterEvent((FriendRecallEvent e) => OnFriendRecallEvent?.Invoke(context, e)); + RegisterEvent((DeviceLoginEvent e) => OnDeviceLoginEvent?.Invoke(context, e)); + RegisterEvent((FriendPokeEvent e) => OnFriendPokeEvent?.Invoke(context, e)); + RegisterEvent((GroupPokeEvent e) => OnGroupPokeEvent?.Invoke(context, e)); + RegisterEvent((GroupEssenceEvent e) => OnGroupEssenceEvent?.Invoke(context, e)); + RegisterEvent((GroupReactionEvent e) => OnGroupReactionEvent?.Invoke(context, e)); + RegisterEvent((GroupNameChangeEvent e) => OnGroupNameChangeEvent?.Invoke(context, e)); + RegisterEvent((GroupTodoEvent e) => OnGroupTodoEvent?.Invoke(context, e)); + RegisterEvent((GroupMemberEnterEvent e) => OnGroupMemberEnterEvent?.Invoke(context, e)); + RegisterEvent((PinChangedEvent e) => OnPinChangedEvent?.Invoke(context, e)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RegisterEvent(Action action) where TEvent : EventBase => _events[typeof(TEvent)] = e => action((TEvent)e); + + internal void PostEvent(EventBase e) + { + Task.Run(() => + { + try + { + if (_events.TryGetValue(e.GetType(), out var action)) action(e); + else PostEvent(new BotLogEvent(Tag, LogLevel.Warning, $"Event {e.GetType().Name} is not registered but pushed to invoker")); + } + catch (Exception ex) + { + PostEvent(new BotLogEvent(Tag, LogLevel.Exception, $"{ex.StackTrace}\n{ex.Message}")); + } + }); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + _events.Clear(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Attributes/BusinessLogicAttribute.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Attributes/BusinessLogicAttribute.cs new file mode 100644 index 0000000..c3ddf9d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Attributes/BusinessLogicAttribute.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Internal.Context.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +internal class BusinessLogicAttribute : Attribute +{ + public string Name { get; } + + public string Description { get; } + + public BusinessLogicAttribute(string name, string description) + { + Name = name; + Description = description; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/BusinessContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/BusinessContext.cs new file mode 100644 index 0000000..9b5e446 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/BusinessContext.cs @@ -0,0 +1,202 @@ +using System.Reflection; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Context.Attributes; +using Lagrange.Core.Internal.Context.Logic; +using Lagrange.Core.Internal.Context.Logic.Implementation; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Packets; +using Lagrange.Core.Internal.Service; +using Lagrange.Core.Utility.Extension; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Context; + +internal class BusinessContext : ContextBase +{ + private const string Tag = nameof(BusinessContext); + + private readonly Dictionary> _businessLogics; + + #region Business Logics + + internal MessagingLogic MessagingLogic { get; private set; } + + internal WtExchangeLogic WtExchangeLogic { get; private set; } + + internal OperationLogic OperationLogic { get; private set; } + + internal CachingLogic CachingLogic { get; private set; } + + #endregion + + public BusinessContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _businessLogics = new Dictionary>(); + + RegisterLogics(); + } + + private void RegisterLogics() + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var logic in assembly.GetTypeByAttributes(out _)) + { + var constructor = logic.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance); + var instance = (LogicBase)constructor[0].Invoke(new object[] { Collection }); + + foreach (var @event in logic.GetCustomAttributes()) + { + if (!_businessLogics.TryGetValue(@event.EventType, out var list)) + { + list = new List(); + _businessLogics.Add(@event.EventType, list); + } + list.Add(instance); // Append logics + } + + switch (instance) + { + case WtExchangeLogic wtExchangeLogic: + WtExchangeLogic = wtExchangeLogic; + break; + case MessagingLogic messagingLogic: + MessagingLogic = messagingLogic; + break; + case OperationLogic operationLogic: + OperationLogic = operationLogic; + break; + case CachingLogic cachingLogic: + CachingLogic = cachingLogic; + break; + } + } + } + + public async Task PushEvent(ProtocolEvent @event) + { + try + { + var packets = Collection.Service.ResolvePacketByEvent(@event); + foreach (var packet in packets) await Collection.Packet.PostPacket(packet); + } + catch + { + return false; + } + + return true; + } + + /// + /// Send Event to the Server, goes through the given context + /// + public async Task> SendEvent(ProtocolEvent @event) + { + await HandleOutgoingEvent(@event); + var result = new List(); + + try + { + var packets = Collection.Service.ResolvePacketByEvent(@event); + foreach (var packet in packets) + { + var returnVal = await Collection.Packet.SendPacket(packet); + var resolved = Collection.Service.ResolveEventByPacket(returnVal); + foreach (var protocol in resolved) + { + await HandleIncomingEvent(protocol); + result.Add(protocol); + } + } + } + catch (Exception e) + { + Collection.Log.LogWarning(Tag, $"Error when processing the event: {@event}"); + Collection.Log.LogWarning(Tag, e.ToString()); + } + + return result; + } + + public async Task HandleIncomingEvent(ProtocolEvent @event) + { + _businessLogics.TryGetValue(typeof(ProtocolEvent), out var baseLogics); + _businessLogics.TryGetValue(@event.GetType(), out var normalLogics); + + var logics = new List(); + if (baseLogics != null) logics.AddRange(baseLogics); + if (normalLogics != null) logics.AddRange(normalLogics); + + foreach (var logic in logics) + { + try + { + await logic.Incoming(@event); + } + catch (Exception e) + { + Collection.Log.LogFatal(Tag, $"Error occurred while handling event {@event.GetType().Name}"); + Collection.Log.LogFatal(Tag, e.Message); + if (e.StackTrace != null) Collection.Log.LogFatal(Tag, e.StackTrace); + } + } + + return true; + } + + public async Task HandleOutgoingEvent(ProtocolEvent @event) + { + _businessLogics.TryGetValue(typeof(ProtocolEvent), out var baseLogics); + _businessLogics.TryGetValue(@event.GetType(), out var normalLogics); + + var logics = new List(); + if (baseLogics != null) logics.AddRange(baseLogics); + if (normalLogics != null) logics.AddRange(normalLogics); + + foreach (var logic in logics) + { + try + { + await logic.Outgoing(@event); + } + catch (Exception e) + { + Collection.Log.LogFatal(Tag, $"Error occurred while handling outgoing event {@event.GetType().Name}"); + Collection.Log.LogFatal(Tag, e.Message); + if (e.StackTrace != null) Collection.Log.LogFatal(Tag, e.StackTrace); + } + } + + return true; + } + + /// + /// Handle the incoming packet with new sequence number. + /// + public async Task HandleServerPacket(SsoPacket packet) + { + bool success = false; + + try + { + var events = Collection.Service.ResolveEventByPacket(packet); + foreach (var @event in events) + { + var isSuccessful = await Collection.Business.HandleIncomingEvent(@event); + if (!isSuccessful) break; + + success = true; + } + } + catch (Exception e) + { + Collection.Log.LogWarning(Tag, $"Error while handling msf push: {packet.PacketType} {packet.Command}"); + Collection.Log.LogWarning(Tag, e.Message); + if (e.StackTrace is { } stackTrace) Collection.Log.LogWarning(Tag, stackTrace); + Collection.Log.LogDebug(Tag, packet.Payload.ToArray().Hex()); + } + + return success; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/ContextBase.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/ContextBase.cs new file mode 100644 index 0000000..efc5ba8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/ContextBase.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Common; + +namespace Lagrange.Core.Internal.Context; + +internal abstract class ContextBase +{ + protected readonly ContextCollection Collection; + + protected readonly BotKeystore Keystore; + + protected readonly BotAppInfo AppInfo; + + protected readonly BotDeviceInfo DeviceInfo; + + protected ContextBase(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + { + Collection = collection; + Keystore = keystore; + AppInfo = appInfo; + DeviceInfo = device; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/ContextCollection.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/ContextCollection.cs new file mode 100644 index 0000000..6f5cf65 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/ContextCollection.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Event; +using TaskScheduler = Lagrange.Core.Utility.TaskScheduler; + +namespace Lagrange.Core.Internal.Context; + +internal class ContextCollection : IDisposable +{ + public PacketContext Packet { get; } + public SocketContext Socket { get; } + public ServiceContext Service { get; } + public BusinessContext Business { get; } + public LogContext Log { get; } + public HighwayContext Highway { get; } + + public BotKeystore Keystore { get; } + public BotAppInfo AppInfo { get; } + public BotDeviceInfo Device { get; } + + public TaskScheduler Scheduler { get; } + public EventInvoker Invoker { get; } + + public ContextCollection(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, BotConfig config, + EventInvoker invoker, TaskScheduler scheduler) + { + Log = new LogContext(this, keystore, appInfo, device, invoker); + Packet = new PacketContext(this, keystore, appInfo, device, config); + Socket = new SocketContext(this, keystore, appInfo, device, config); + Service = new ServiceContext(this, keystore, appInfo, device); + Business = new BusinessContext(this, keystore, appInfo, device); + Highway = new HighwayContext(this, keystore, appInfo, device, config); + + Keystore = keystore; + AppInfo = appInfo; + Device = device; + + Scheduler = scheduler; + Invoker = invoker; + } + + public void Dispose() + { + Socket.Dispose(); + Highway.Dispose(); + Invoker.Dispose(); + Scheduler.Dispose(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/HighwayContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/HighwayContext.cs new file mode 100644 index 0000000..8f7789b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/HighwayContext.cs @@ -0,0 +1,245 @@ +using System.Reflection; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Context.Uploader; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Highway; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using ProtoBuf.Meta; + +namespace Lagrange.Core.Internal.Context; + +/// +/// Provides BDH (Big-Data Highway) Operation +/// +internal class HighwayContext : ContextBase, IDisposable +{ + private const string Tag = nameof(HighwayContext); + + private static readonly RuntimeTypeModel Serializer; + + private uint _sequence; + private Uri? _uri; + + private readonly Dictionary _uploaders; + private readonly HttpClient _client; + private readonly int _chunkSize; + private readonly uint _concurrent; + + static HighwayContext() + { + Serializer = RuntimeTypeModel.Create(); + Serializer.UseImplicitZeroDefaults = false; + } + + public HighwayContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, BotConfig config) + : base(collection, keystore, appInfo, device) + { + _uploaders = new Dictionary(); + foreach (var impl in Assembly.GetExecutingAssembly().GetImplementations()) + { + var attribute = impl.GetCustomAttribute(); + if (attribute != null) _uploaders[attribute.Entity] = (IHighwayUploader)impl.CreateInstance(false); + } + + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (_, _, _, _) => true, + }; + + _client = new HttpClient(handler); + _client.DefaultRequestHeaders.Add("Accept-Encoding", "identity"); + _client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2)"); + + _sequence = 0; + _chunkSize = (int)config.HighwayChunkSize; + _concurrent = config.HighwayConcurrent; + } + + public async Task UploadResources(MessageChain chain) + { + foreach (var entity in chain) + { + if (_uploaders.TryGetValue(entity.GetType(), out var uploader)) + { + try + { + if (chain.IsGroup) await uploader.UploadGroup(Collection, chain, entity); + else await uploader.UploadPrivate(Collection, chain, entity); + } + catch + { + Collection.Log.LogFatal(Tag, $"Upload resources for {entity.GetType().Name} failed"); + } + } + } + } + + public async Task ManualUploadEntity(IMessageEntity entity) + { + if (_uploaders.TryGetValue(entity.GetType(), out var uploader)) + { + try + { + uint uin = Collection.Keystore.Uin; + string uid = Collection.Keystore.Uid ?? ""; + var chain = new MessageChain(uin, uid, uid) { entity }; + + await uploader.UploadPrivate(Collection, chain, entity); + } + catch + { + Collection.Log.LogFatal(Tag, $"Upload resources for {entity.GetType().Name} failed"); + } + } + } + + public async Task UploadSrcByStreamAsync(int commonId, Stream data, byte[] ticket, byte[] md5, byte[]? extendInfo = null) + { + if (_uri == null) + { + var highwayUrlEvent = await Collection.Business.SendEvent(HighwayUrlEvent.Create()); + var result = (HighwayUrlEvent)highwayUrlEvent[0]; + _uri = result.HighwayUrls[1][0]; + } + + bool success = true; + var upBlocks = new List(); + + long fileSize = data.Length; + int offset = 0; + + data.Seek(0, SeekOrigin.Begin); + while (offset < fileSize) + { + var buffer = new byte[Math.Min(_chunkSize, fileSize - offset)]; + int payload = await data.ReadAsync(buffer.AsMemory()); + uint uin = Collection.Keystore.Uin; + uint sequence = Interlocked.Increment(ref _sequence); + var reqBody = new UpBlock(commonId, uin, sequence, (ulong)fileSize, (ulong)offset, ticket, md5, buffer, extendInfo); + upBlocks.Add(reqBody); + offset += payload; + + if (upBlocks.Count >= _concurrent || data.Position == data.Length) + { + var tasks = upBlocks.Select(x => SendUpBlockAsync(x, _uri)).ToArray(); + var results = await Task.WhenAll(tasks); + success &= results.All(x => x); + + upBlocks.Clear(); + } + } + + return success; + } + + private async Task SendUpBlockAsync(UpBlock upBlock, Uri server) + { + var head = new DataHighwayHead + { + Version = 1, + Uin = upBlock.Uin.ToString(), + Command = "PicUp.DataUp", + Seq = upBlock.Sequence, + AppId = (uint)AppInfo.SubAppId, + DataFlag = 16, + CommandId = (uint)upBlock.CommandId, + }; + var segHead = new SegHead + { + Filesize = upBlock.FileSize, + DataOffset = upBlock.Offset, + DataLength = (uint)upBlock.Block.Length, + ServiceTicket = upBlock.Ticket, + Md5 = (await upBlock.Block.Md5Async()).UnHex(), + FileMd5 = upBlock.FileMd5, + }; + var loginHead = new LoginSigHead + { + Uint32LoginSigType = 8, + BytesLoginSig = Collection.Keystore.Session.Tgt, + AppId = (uint)Collection.AppInfo.AppId + }; + var highwayHead = new ReqDataHighwayHead + { + MsgBaseHead = head, + MsgSegHead = segHead, + BytesReqExtendInfo = upBlock.ExtendInfo, + Timestamp = upBlock.Timestamp, + MsgLoginSigHead = loginHead + }; + + bool isEnd = upBlock.Offset + (ulong)upBlock.Block.Length == upBlock.FileSize; + var payload = await SendPacketAsync(highwayHead, new BinaryPacket(upBlock.Block), server, isEnd); + var (respHead, resp) = ParsePacket(payload); + + Collection.Log.LogDebug(Tag, $"Highway Block Result: {respHead.ErrorCode} | {respHead.MsgSegHead?.RetCode} | {respHead.BytesRspExtendInfo?.Hex()} | {resp.ToArray().Hex()}"); + + return respHead.ErrorCode == 0; + } + + private Task SendPacketAsync(ReqDataHighwayHead head, BinaryPacket buffer, Uri server, bool end = true) + { + using var stream = new MemoryStream(); + Serializer.Serialize(stream, head); + + var writer = new BinaryPacket() + .WriteByte(0x28) // packet start + .WriteInt((int)stream.Length) + .WriteInt((int)buffer.Length) + .WriteBytes(stream.ToArray()) + .WritePacket(buffer) + .WriteByte(0x29); // packet end + + return SendDataAsync(writer.ToArray(), server, end); + } + + private static (RespDataHighwayHead, BinaryPacket) ParsePacket(BinaryPacket packet) + { + if (packet.ReadByte() == 0x28) + { + int headLength = packet.ReadInt(); + int bodyLength = packet.ReadInt(); + var head = Serializer.Deserialize(packet.ReadBytes(headLength)); + var body = packet.ReadPacket(bodyLength); + + if (packet.ReadByte() == 0x29) return (head, body); + } + + throw new InvalidOperationException("Invalid packet"); + } + + private async Task SendDataAsync(byte[] packet, Uri server, bool end) + { + var content = new ByteArrayContent(packet); + var request = new HttpRequestMessage(HttpMethod.Post, server) + { + Content = content, + Headers = + { + { "Connection" , end ? "close" : "keep-alive" }, + } + }; + var response = await _client.SendAsync(request); + var data = await response.Content.ReadAsByteArrayAsync(); + return new BinaryPacket(data); + } + + private record struct UpBlock( + int CommandId, + uint Uin, + uint Sequence, + ulong FileSize, + ulong Offset, + byte[] Ticket, + byte[] FileMd5, + byte[] Block, + byte[]? ExtendInfo = null, + ulong Timestamp = 0); + + public void Dispose() + { + _client.Dispose(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/LogContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/LogContext.cs new file mode 100644 index 0000000..6ee593a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/LogContext.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Event; +using Lagrange.Core.Event.EventArg; + +namespace Lagrange.Core.Internal.Context; + +/// +/// Log context, all the logs will be dispatched to this context and then to the . +/// +internal class LogContext : ContextBase +{ + private readonly EventInvoker _invoker; + + public LogContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, EventInvoker invoker) + : base(collection, keystore, appInfo, device) => _invoker = invoker; + + public void LogDebug(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Debug, message)); + + public void LogVerbose(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Verbose, message)); + + public void LogInfo(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Information, message)); + + public void LogWarning(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Warning, message)); + + public void LogFatal(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Fatal, message)); + + public void Log(string tag, LogLevel level, string message) => + _invoker.PostEvent(new BotLogEvent(tag, level, message)); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/CachingLogic.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/CachingLogic.cs new file mode 100644 index 0000000..3fbafcd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/CachingLogic.cs @@ -0,0 +1,229 @@ +using System.Collections.Concurrent; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Context.Attributes; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Notify; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Service; + +namespace Lagrange.Core.Internal.Context.Logic.Implementation; + +[EventSubscribe(typeof(GroupSysDecreaseEvent))] +[EventSubscribe(typeof(GroupSysIncreaseEvent))] +[EventSubscribe(typeof(GroupSysAdminEvent))] +[BusinessLogic("CachingLogic", "Cache Uin to Uid")] +internal class CachingLogic : LogicBase +{ + private const string Tag = nameof(CachingLogic); + + private readonly Dictionary _uinToUid; + private readonly List _cachedGroups; + private readonly List _cachedGroupEntities; + + private readonly List _cachedFriends; + private readonly Dictionary> _cachedGroupMembers; + + private readonly ConcurrentDictionary _cacheUsers; + + private readonly Dictionary _cacheFaceEntities; + private readonly List _cacheSuperFaceId; + + internal CachingLogic(ContextCollection collection) : base(collection) + { + _uinToUid = new Dictionary(); + _cachedGroups = new List(); + _cachedGroupEntities = new List(); + + _cachedFriends = new List(); + _cachedGroupMembers = new Dictionary>(); + + _cacheUsers = new ConcurrentDictionary(); + + _cacheFaceEntities = new Dictionary(); + _cacheSuperFaceId = new List(); + } + + public override Task Incoming(ProtocolEvent e) + { + return e switch + { + GroupSysDecreaseEvent groupSysDecreaseEvent when groupSysDecreaseEvent.MemberUid != Collection.Keystore.Uid => CacheUid(groupSysDecreaseEvent.GroupUin, true), + GroupSysIncreaseEvent groupSysIncreaseEvent => CacheUid(groupSysIncreaseEvent.GroupUin, true), + GroupSysAdminEvent groupSysAdminEvent => CacheUid(groupSysAdminEvent.GroupUin, true), + _ => Task.CompletedTask, + }; + } + + public async Task> GetCachedGroups(bool refreshCache) + { + if (_cachedGroupEntities.Count == 0 || refreshCache) + { + _cachedGroupEntities.Clear(); + + var fetchGroupsEvent = FetchGroupsEvent.Create(); + var events = await Collection.Business.SendEvent(fetchGroupsEvent); + var groups = ((FetchGroupsEvent)events[0]).Groups; + + _cachedGroupEntities.AddRange(groups); + return groups; + } + + return _cachedGroupEntities; + } + + public async Task ResolveUid(uint? groupUin, uint friendUin) + { + if (_uinToUid.Count == 0) await ResolveFriendsUidAndFriendGroups(); + if (groupUin == null) return _uinToUid.GetValueOrDefault(friendUin); + + await CacheUid(groupUin.Value); + + return _uinToUid.GetValueOrDefault(friendUin); + } + + public async Task ResolveUin(uint? groupUin, string friendUid, bool force = false) + { + if (_uinToUid.Count == 0) await ResolveFriendsUidAndFriendGroups(); + if (groupUin == null) return _uinToUid.FirstOrDefault(x => x.Value == friendUid).Key; + + await CacheUid(groupUin.Value, force); + + return _uinToUid.FirstOrDefault(x => x.Value == friendUid).Key; + } + + public async Task> GetCachedMembers(uint groupUin, bool refreshCache) + { + if (!_cachedGroupMembers.TryGetValue(groupUin, out var members) || refreshCache) + { + await ResolveMembersUid(groupUin); + return _cachedGroupMembers.TryGetValue(groupUin, out members) ? members : new List(); + } + + return members; + } + + public async Task> GetCachedFriends(bool refreshCache) + { + if (_cachedFriends.Count == 0 || refreshCache) await ResolveFriendsUidAndFriendGroups(); + return _cachedFriends; + } + + public async Task GetCachedUsers(uint uin, bool refreshCache) + { + if (!_cacheUsers.ContainsKey(uin) || refreshCache) await ResolveUser(uin); + if (!_cacheUsers.TryGetValue(uin, out BotUserInfo? info)) return null; + return info; + } + + private async Task CacheUid(uint groupUin, bool force = false) + { + if (!_cachedGroups.Contains(groupUin) || force) + { + Collection.Log.LogVerbose(Tag, $"Caching group members: {groupUin}"); + await ResolveMembersUid(groupUin); + _cachedGroups.Add(groupUin); + } + } + + private async Task ResolveFriendsUidAndFriendGroups() + { + uint? next = null; + var friends = new List(); + var friendGroups = new Dictionary(); + do + { + var @event = FetchFriendsEvent.Create(next); + var results = await Collection.Business.SendEvent(@event); + + if (results.Count == 0) + { + Collection.Log.LogWarning(Tag, "Failed to resolve friends uid and cache."); + return; + } + + var result = (FetchFriendsEvent)results[0]; + + foreach ((uint id, string name) in result.FriendGroups) friendGroups[id] = name; + + foreach (var friend in result.Friends) + { + friend.Group = new(friend.Group.GroupId, friendGroups[friend.Group.GroupId]); + } + + friends.AddRange(result.Friends); + + next = result.NextUin; + } while (next.HasValue); + + foreach (var friend in friends) _uinToUid.TryAdd(friend.Uin, friend.Uid); + + _cachedFriends.Clear(); + _cachedFriends.AddRange(friends); + } + + private async Task ResolveMembersUid(uint groupUin) + { + var fetchFriendsEvent = FetchMembersEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(fetchFriendsEvent); + + if (events.Count != 0) + { + var @event = (FetchMembersEvent)events[0]; + string? token = @event.Token; + + while (token != null) + { + var next = FetchMembersEvent.Create(groupUin, token); + var results = await Collection.Business.SendEvent(next); + @event.Members.AddRange(((FetchMembersEvent)results[0]).Members); + token = ((FetchMembersEvent)results[0]).Token; + } + + foreach (var member in @event.Members) _uinToUid.TryAdd(member.Uin, member.Uid); + _cachedGroupMembers[groupUin] = @event.Members; + } + else + { + _cachedGroupMembers[groupUin] = new List(); + Collection.Log.LogWarning(Tag, $"Failed to resolve group {groupUin} members uid and cache."); + } + } + + private async Task ResolveUser(uint uin) + { + var events = await Collection.Business.SendEvent(FetchUserInfoEvent.Create(uin)); + + if (events.Count != 0 && events[0] is FetchUserInfoEvent { } @event) + { + _cacheUsers.AddOrUpdate(uin, @event.UserInfo, (_key, _value) => @event.UserInfo); + } + } + + private async Task ResolveEmojiCache() + { + var fetchSysEmojisEvent = FetchFullSysFacesEvent.Create(); + var events = await Collection.Business.SendEvent(fetchSysEmojisEvent); + var emojiPacks = ((FetchFullSysFacesEvent)events[0]).FacePacks; + + emojiPacks + .SelectMany(pack => pack.Emojis) + .Where(emoji => uint.TryParse(emoji.QSid, out _)) + .ToList() + .ForEach(emoji => _cacheFaceEntities[uint.Parse(emoji.QSid)] = emoji); + + _cacheSuperFaceId.AddRange(emojiPacks + .SelectMany(emojiPack => emojiPack.GetUniqueSuperQSids(new[] { (1, 1) }))); + } + + public async Task GetCachedIsSuperFaceId(uint id) + { + if (!_cacheSuperFaceId.Any()) await ResolveEmojiCache(); + return _cacheSuperFaceId.Contains(id); + } + + public async Task GetCachedFaceEntity(uint faceId) + { + if (!_cacheFaceEntities.ContainsKey(faceId)) await ResolveEmojiCache(); + return _cacheFaceEntities.GetValueOrDefault(faceId); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs new file mode 100644 index 0000000..3777ef5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs @@ -0,0 +1,527 @@ +using Lagrange.Core.Event; +using Lagrange.Core.Event.EventArg; +using Lagrange.Core.Internal.Context.Attributes; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Event.Notify; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Service; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Message.Filter; +using FriendPokeEvent = Lagrange.Core.Event.EventArg.FriendPokeEvent; +using GroupPokeEvent = Lagrange.Core.Event.EventArg.GroupPokeEvent; + +namespace Lagrange.Core.Internal.Context.Logic.Implementation; + +[EventSubscribe(typeof(PushMessageEvent))] +[EventSubscribe(typeof(SendMessageEvent))] +[EventSubscribe(typeof(MultiMsgUploadEvent))] +[EventSubscribe(typeof(GetRoamMessageEvent))] +[EventSubscribe(typeof(GetGroupMessageEvent))] +[EventSubscribe(typeof(GroupSysInviteEvent))] +[EventSubscribe(typeof(GroupSysAdminEvent))] +[EventSubscribe(typeof(GroupSysIncreaseEvent))] +[EventSubscribe(typeof(GroupSysDecreaseEvent))] +[EventSubscribe(typeof(GroupSysMuteEvent))] +[EventSubscribe(typeof(GroupSysMemberMuteEvent))] +[EventSubscribe(typeof(GroupSysRecallEvent))] +[EventSubscribe(typeof(GroupSysRequestJoinEvent))] +[EventSubscribe(typeof(GroupSysRequestInvitationEvent))] +[EventSubscribe(typeof(GroupSysEssenceEvent))] +[EventSubscribe(typeof(GroupSysPokeEvent))] +[EventSubscribe(typeof(GroupSysReactionEvent))] +[EventSubscribe(typeof(GroupSysNameChangeEvent))] +[EventSubscribe(typeof(FriendSysRecallEvent))] +[EventSubscribe(typeof(FriendSysRequestEvent))] +[EventSubscribe(typeof(GroupSysMemberEnterEvent))] +[EventSubscribe(typeof(FriendSysPokeEvent))] +[EventSubscribe(typeof(LoginNotifyEvent))] +[EventSubscribe(typeof(MultiMsgDownloadEvent))] +[EventSubscribe(typeof(GroupSysTodoEvent))] +[EventSubscribe(typeof(SysPinChangedEvent))] +[EventSubscribe(typeof(FetchPinsEvent))] +[EventSubscribe(typeof(SetPinFriendEvent))] +[BusinessLogic("MessagingLogic", "Manage the receiving and sending of messages and notifications")] +internal class MessagingLogic : LogicBase +{ + private const string Tag = nameof(MessagingLogic); + + internal MessagingLogic(ContextCollection collection) : base(collection) { } + + public override async Task Incoming(ProtocolEvent e) + { + switch (e) + { + case PushMessageEvent push: + { + if (push.Chain.Count == 0) return; + await ResolveIncomingChain(push.Chain); + await ResolveChainMetadata(push.Chain); + MessageFilter.Filter(push.Chain); + + var chain = push.Chain; + Collection.Log.LogVerbose(Tag, chain.ToPreviewString()); + + EventBase args = push.Chain.Type switch + { + MessageChain.MessageType.Friend => new FriendMessageEvent(chain), + MessageChain.MessageType.Group => new GroupMessageEvent(chain), + MessageChain.MessageType.Temp => new TempMessageEvent(chain), + _ => throw new ArgumentOutOfRangeException() + }; + Collection.Invoker.PostEvent(args); + + break; + } + case GetRoamMessageEvent get: + { + foreach (var chain in get.Chains) + { + if (chain.Count == 0) return; + await ResolveIncomingChain(chain); + await ResolveChainMetadata(chain); + MessageFilter.Filter(chain); + } + break; + } + case GetGroupMessageEvent get: + { + foreach (var chain in get.Chains) + { + if (chain.Count == 0) return; + await ResolveIncomingChain(chain); + await ResolveChainMetadata(chain); + MessageFilter.Filter(chain); + } + break; + } + case GroupSysInviteEvent invite: + { + uint invitorUin = await Collection.Business.CachingLogic.ResolveUin(null, invite.InvitorUid) ?? 0; + var inviteArgs = new GroupInvitationEvent(invite.GroupUin, invitorUin); + Collection.Invoker.PostEvent(inviteArgs); + break; + } + case GroupSysAdminEvent admin: + { + uint adminUin = await Collection.Business.CachingLogic.ResolveUin(admin.GroupUin, admin.Uid) ?? 0; + var adminArgs = new GroupAdminChangedEvent(admin.GroupUin, adminUin, admin.IsPromoted); + Collection.Invoker.PostEvent(adminArgs); + break; + } + case GroupSysIncreaseEvent increase: + { + uint memberUin = await Collection.Business.CachingLogic.ResolveUin(increase.GroupUin, increase.MemberUid, true) ?? 0; + uint? invitorUin = null; + if (increase.InvitorUid != null) invitorUin = await Collection.Business.CachingLogic.ResolveUin(increase.GroupUin, increase.InvitorUid); + var increaseArgs = new GroupMemberIncreaseEvent(increase.GroupUin, memberUin, invitorUin, increase.Type); + Collection.Invoker.PostEvent(increaseArgs); + break; + } + case GroupSysDecreaseEvent decrease: + { + uint memberUin = await Collection.Business.CachingLogic.ResolveUin(decrease.GroupUin, decrease.MemberUid) ?? 0; + uint? operatorUin = null; + if (decrease.OperatorUid != null) operatorUin = await Collection.Business.CachingLogic.ResolveUin(decrease.GroupUin, decrease.OperatorUid); + var decreaseArgs = new GroupMemberDecreaseEvent(decrease.GroupUin, memberUin, operatorUin, decrease.Type); + Collection.Invoker.PostEvent(decreaseArgs); + break; + } + case GroupSysEssenceEvent essence: + { + var essenceArgs = new GroupEssenceEvent(essence.GroupUin, essence.Sequence, essence.Random, essence.SetFlag, essence.FromUin, essence.OperatorUin); + Collection.Invoker.PostEvent(essenceArgs); + break; + } + case GroupSysPokeEvent poke: + { + var pokeArgs = new GroupPokeEvent(poke.GroupUin, poke.OperatorUin, poke.TargetUin, poke.Action, poke.Suffix, poke.ActionImgUrl); + Collection.Invoker.PostEvent(pokeArgs); + break; + } + case GroupSysReactionEvent reaction: + { + uint operatorUin = await Collection.Business.CachingLogic.ResolveUin(reaction.TargetGroupUin, reaction.OperatorUid) ?? 0; + var pokeArgs = new GroupReactionEvent(reaction.TargetGroupUin, reaction.TargetSequence, operatorUin, reaction.IsAdd, reaction.Code, reaction.Count); + Collection.Invoker.PostEvent(pokeArgs); + break; + } + case GroupSysNameChangeEvent nameChange: + { + var pokeArgs = new GroupNameChangeEvent(nameChange.GroupUin, nameChange.Name); + Collection.Invoker.PostEvent(pokeArgs); + break; + } + case FriendSysRequestEvent info: + { + var requestArgs = new FriendRequestEvent(info.SourceUin, info.SourceUid, info.Message, info.Source); + Collection.Invoker.PostEvent(requestArgs); + break; + } + case GroupSysMemberEnterEvent info: + { + var @event = new GroupMemberEnterEvent(info.GroupUin, info.GroupMemberUin, info.StyleId); + Collection.Invoker.PostEvent(@event); + break; + } + case GroupSysMuteEvent groupMute: + { + uint? operatorUin = null; + if (groupMute.OperatorUid != null) operatorUin = await Collection.Business.CachingLogic.ResolveUin(groupMute.GroupUin, groupMute.OperatorUid); + var muteArgs = new GroupMuteEvent(groupMute.GroupUin, operatorUin, groupMute.IsMuted); + Collection.Invoker.PostEvent(muteArgs); + break; + } + case GroupSysMemberMuteEvent memberMute: + { + uint memberUin = await Collection.Business.CachingLogic.ResolveUin(memberMute.GroupUin, memberMute.TargetUid) ?? 0; + uint? operatorUin = null; + if (memberMute.OperatorUid != null) operatorUin = await Collection.Business.CachingLogic.ResolveUin(memberMute.GroupUin, memberMute.OperatorUid); + var muteArgs = new GroupMemberMuteEvent(memberMute.GroupUin, memberUin, operatorUin, memberMute.Duration); + Collection.Invoker.PostEvent(muteArgs); + break; + } + case GroupSysRecallEvent recall: + { + uint authorUin = await Collection.Business.CachingLogic.ResolveUin(recall.GroupUin, recall.AuthorUid) ?? 0; + uint operatorUin = 0; + if (recall.OperatorUid != null) operatorUin = await Collection.Business.CachingLogic.ResolveUin(recall.GroupUin, recall.OperatorUid) ?? 0; + var recallArgs = new GroupRecallEvent(recall.GroupUin, authorUin, operatorUin, recall.Sequence, recall.Time, recall.Random, recall.Tip); + Collection.Invoker.PostEvent(recallArgs); + break; + } + case GroupSysRequestJoinEvent join: + { + var fetchUidEvent = FetchUserInfoEvent.Create(join.TargetUid); + var results = await Collection.Business.SendEvent(fetchUidEvent); + uint targetUin = results.Count == 0 ? 0 : ((FetchUserInfoEvent)results[0]).UserInfo.Uin; + + var joinArgs = new GroupJoinRequestEvent(join.GroupUin, targetUin); + Collection.Invoker.PostEvent(joinArgs); + break; + } + case GroupSysRequestInvitationEvent invitation: + { + uint invitorUin = await Collection.Business.CachingLogic.ResolveUin(invitation.GroupUin, invitation.InvitorUid) ?? 0; + + var fetchUidEvent = FetchUserInfoEvent.Create(invitation.TargetUid); + var results = await Collection.Business.SendEvent(fetchUidEvent); + uint targetUin = results.Count == 0 ? 0 : ((FetchUserInfoEvent)results[0]).UserInfo.Uin; + + var invitationArgs = new GroupInvitationRequestEvent(invitation.GroupUin, targetUin, invitorUin); + Collection.Invoker.PostEvent(invitationArgs); + break; + } + case FriendSysRecallEvent recall: + { + uint fromUin = await Collection.Business.CachingLogic.ResolveUin(null, recall.FromUid) ?? 0; + var recallArgs = new FriendRecallEvent(fromUin, recall.ClientSequence, recall.Time, recall.Random, recall.Tip); + Collection.Invoker.PostEvent(recallArgs); + break; + } + case FriendSysPokeEvent poke: + { + var pokeArgs = new FriendPokeEvent(poke.OperatorUin, poke.TargetUin, poke.Action, poke.Suffix, poke.ActionImgUrl); + Collection.Invoker.PostEvent(pokeArgs); + break; + } + case LoginNotifyEvent login: + { + var deviceArgs = new DeviceLoginEvent(login.IsLogin, login.AppId, login.Tag, login.Message); + Collection.Invoker.PostEvent(deviceArgs); + break; + } + case MultiMsgDownloadEvent multi: + { + if (multi.Chains != null) + { + foreach (var chain in multi.Chains) + { + if (chain.Count == 0) continue; + await ResolveIncomingChain(chain); + MessageFilter.Filter(chain); + } + } + break; + } + case GroupSysTodoEvent todo: + { + uint uin = await Collection.Business.CachingLogic.ResolveUin(todo.GroupUin, todo.OperatorUid) ?? 0; + + Collection.Invoker.PostEvent(new GroupTodoEvent(todo.GroupUin, uin)); + break; + } + case SysPinChangedEvent pin: + { + uint uin = pin.GroupUin ?? await Collection.Business.CachingLogic.ResolveUin(null, pin.Uid) ?? 0; + + Collection.Invoker.PostEvent(new PinChangedEvent( + pin.GroupUin == null ? PinChangedEvent.ChatType.Friend : PinChangedEvent.ChatType.Group, + uin, + pin.IsPin + )); + break; + } + case FetchPinsEvent pins: + { + foreach (var friendUid in pins.FriendUids) + { + pins.FriendUins.Add(await Collection.Business.CachingLogic.ResolveUin(null, friendUid) ?? 0); + } + + break; + } + } + } + + public override async Task Outgoing(ProtocolEvent e) + { + switch (e) + { + case MultiMsgUploadEvent { Chains: { } chains }: + { + foreach (var chain in chains) + { + await ResolveChainMetadata(chain); + await ResolveOutgoingChain(chain); + await Collection.Highway.UploadResources(chain); + } + break; + } + case SendMessageEvent send: // resolve Uin to Uid + { + await ResolveChainMetadata(send.Chain); + await ResolveOutgoingChain(send.Chain); + await Collection.Highway.UploadResources(send.Chain); + break; + } + case SetPinFriendEvent pinFriend: // resolve Uin to Uid + { + pinFriend.Uid = await Collection.Business.CachingLogic.ResolveUid(null, pinFriend.Uin) + ?? throw new Exception(); + + break; + } + } + } + + private async Task ResolveIncomingChain(MessageChain chain) + { + foreach (var entity in chain) + { + switch (entity) + { + case FileEntity { FileHash: not null, FileUuid: not null } file: // private + { + var @event = FileDownloadEvent.Create(file.FileUuid, file.FileHash, chain.Uid, chain.SelfUid); + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (FileDownloadEvent)results[0]; + file.FileUrl = result.FileUrl; + } + + break; + } + case FileEntity { FileId: not null } file when chain.GroupUin is not null: // group + { + var @event = GroupFSDownloadEvent.Create(chain.GroupUin.Value, file.FileId); + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (GroupFSDownloadEvent)results[0]; + file.FileUrl = result.FileUrl; + } + + break; + } + case MultiMsgEntity { ResId: not null } multi: + { + var @event = MultiMsgDownloadEvent.Create(chain.Uid ?? "", multi.ResId); + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (MultiMsgDownloadEvent)results[0]; + multi.Chains.AddRange((IEnumerable?)result.Chains ?? Array.Empty()); + } + + break; + } + case RecordEntity { MsgInfo: not null } record: + { + var @event = chain.IsGroup + ? RecordGroupDownloadEvent.Create(chain.GroupUin ?? 0, record.MsgInfo) + : RecordDownloadEvent.Create(chain.Uid ?? string.Empty, record.MsgInfo); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (RecordDownloadEvent)results[0]; + record.AudioUrl = result.AudioUrl; + } + + break; + } + case RecordEntity { AudioUuid: not null } record: + { + var @event = chain.IsGroup + ? RecordGroupDownloadEvent.Create(chain.GroupUin ?? 0, record.AudioUuid) + : RecordDownloadEvent.Create(chain.Uid ?? string.Empty, record.AudioUuid); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (RecordDownloadEvent)results[0]; + record.AudioUrl = result.AudioUrl; + } + + break; + } + case VideoEntity { VideoUuid: not null } video: + { + string uid = (chain.IsGroup + ? await Collection.Business.CachingLogic.ResolveUid(chain.GroupUin, chain.FriendUin) + : chain.Uid) ?? ""; + var @event = VideoDownloadEvent.Create(video.VideoUuid, uid, video.FilePath, "", "", chain.IsGroup); + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (VideoDownloadEvent)results[0]; + video.VideoUrl = result.AudioUrl; + } + + break; + } + case ImageEntity image when !image.ImageUrl.Contains("&rkey=") && image.MsgInfo is not null: + { + var @event = image.IsGroup + ? ImageGroupDownloadEvent.Create(chain.GroupUin ?? 0, image.MsgInfo) + : ImageDownloadEvent.Create(chain.Uid ?? string.Empty, image.MsgInfo); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (ImageDownloadEvent)results[0]; + image.ImageUrl = result.ImageUrl; + } + + break; + } + } + } + } + + private async Task ResolveOutgoingChain(MessageChain chain) + { + foreach (var entity in chain) + { + switch (entity) + { + case FaceEntity face: + { + var cache = Collection.Business.CachingLogic; + face.SysFaceEntry ??= await cache.GetCachedFaceEntity(face.FaceId); + break; + } + case BounceFaceEntity bounceFace: + { + var cache = Collection.Business.CachingLogic; + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (bounceFace.Name != null) + break; + + string name = (await cache.GetCachedFaceEntity(bounceFace.FaceId))?.QDes ?? string.Empty; + + // Because the name is used as a preview text, it should not start with '/' + // But the QDes of the face may start with '/', so remove it + if (name.StartsWith('/')) + name = name[1..]; + + bounceFace.Name = name; + break; + } + case ForwardEntity forward when forward.TargetUin != 0: + { + var cache = Collection.Business.CachingLogic; + forward.Uid = await cache.ResolveUid(chain.GroupUin, forward.TargetUin) ?? throw new Exception($"Failed to resolve Uid for Uin {forward.TargetUin}"); + + break; + } + case MentionEntity mention when mention.Uin != 0: + { + var cache = Collection.Business.CachingLogic; + mention.Uid = await cache.ResolveUid(chain.GroupUin, mention.Uin) ?? throw new Exception($"Failed to resolve Uid for Uin {mention.Uin}"); + + if (chain is { IsGroup: true, GroupUin: not null } && mention.Name is null) + { + var members = await Collection.Business.CachingLogic.GetCachedMembers(chain.GroupUin.Value, false); + var member = members.FirstOrDefault(x => x.Uin == mention.Uin); + if (member != null) mention.Name = $"@{member.MemberCard ?? member.MemberName}"; + } + else if (chain is { IsGroup: false } && mention.Name is null) + { + var friends = await Collection.Business.CachingLogic.GetCachedFriends(false); + string? friend = friends.FirstOrDefault(x => x.Uin == mention.Uin)?.Nickname; + if (friend != null) mention.Name = $"@{friend}"; + } + + break; + } + case MultiMsgEntity { ResId: null } multiMsg: + { + if (chain.GroupUin != null) foreach (var c in multiMsg.Chains) c.GroupUin = chain.GroupUin; + + var multiMsgEvent = MultiMsgUploadEvent.Create(chain.GroupUin, multiMsg.Chains); + var results = await Collection.Business.SendEvent(multiMsgEvent); + if (results.Count != 0) + { + var result = (MultiMsgUploadEvent)results[0]; + multiMsg.ResId = result.ResId; + } + break; + } + case MultiMsgEntity { ResId: not null, Chains.Count: 0 } multiMsg: + { + var @event = MultiMsgDownloadEvent.Create(chain.Uid ?? "", multiMsg.ResId); + var results = await Collection.Business.SendEvent(@event); + if (results.Count != 0) + { + var result = (MultiMsgDownloadEvent)results[0]; + multiMsg.Chains.AddRange((IEnumerable?)result.Chains ?? Array.Empty()); + } + break; + } + } + } + } + + /// + /// Resolve the or for the + /// for both Incoming and Outgoing MessageChain + /// + /// The target chain + private async Task ResolveChainMetadata(MessageChain chain) + { + if (chain is { IsGroup: true, GroupUin: not null }) + { + var groups = await Collection.Business.CachingLogic.GetCachedMembers(chain.GroupUin.Value, false); + chain.GroupMemberInfo = chain.FriendUin == 0 + ? groups.FirstOrDefault(x => x.Uin == Collection.Keystore.Uin) + : groups.FirstOrDefault(x => x.Uin == chain.FriendUin); + + chain.Uid ??= chain.GroupMemberInfo?.Uid; + } + else + { + var friends = await Collection.Business.CachingLogic.GetCachedFriends(false); + if (friends.FirstOrDefault(x => x.Uin == chain.FriendUin) is { } friend) + { + chain.FriendInfo = friend; + chain.Uid ??= friend.Uid; + } + } + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs new file mode 100644 index 0000000..231f4dc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs @@ -0,0 +1,868 @@ +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Context.Attributes; +using Lagrange.Core.Internal.Context.Uploader; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Highway; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Logic.Implementation; + +[BusinessLogic("OperationLogic", "Manage the user operation of the bot")] +internal class OperationLogic : LogicBase +{ + private const string Tag = nameof(OperationLogic); + + internal OperationLogic(ContextCollection collection) : base(collection) { } + + public async Task> GetCookies(List domains) + { + var fetchCookieEvent = FetchCookieEvent.Create(domains); + var events = await Collection.Business.SendEvent(fetchCookieEvent); + return events.Count != 0 ? ((FetchCookieEvent)events[0]).Cookies : new List(); + } + + public Task> FetchFriends(bool refreshCache = false) => + Collection.Business.CachingLogic.GetCachedFriends(refreshCache); + + public Task> FetchMembers(uint groupUin, bool refreshCache = false) => + Collection.Business.CachingLogic.GetCachedMembers(groupUin, refreshCache); + + public Task> FetchGroups(bool refreshCache) => + Collection.Business.CachingLogic.GetCachedGroups(refreshCache); + + public async Task SendMessage(MessageChain chain) + { + uint clientSeq = chain.ClientSequence; + ulong messageId = chain.MessageId; + + var sendMessageEvent = SendMessageEvent.Create(chain); + var events = await Collection.Business.SendEvent(sendMessageEvent); + if (events.Count == 0) return new MessageResult { Result = 9057 }; + + var result = ((SendMessageEvent)events[0]).MsgResult; + result.ClientSequence = clientSeq; + result.MessageId = messageId; + return result; + } + + public async Task MuteGroupMember(uint groupUin, uint targetUin, uint duration) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null) return false; + + var muteGroupMemberEvent = GroupMuteMemberEvent.Create(groupUin, duration, uid); + var events = await Collection.Business.SendEvent(muteGroupMemberEvent); + return events.Count != 0 && ((GroupMuteMemberEvent)events[0]).ResultCode == 0; + } + + public async Task MuteGroupGlobal(uint groupUin, bool isMute) + { + var muteGroupMemberEvent = GroupMuteGlobalEvent.Create(groupUin, isMute); + var events = await Collection.Business.SendEvent(muteGroupMemberEvent); + return events.Count != 0 && ((GroupMuteGlobalEvent)events[0]).ResultCode == 0; + } + + public async Task KickGroupMember(uint groupUin, uint targetUin, bool rejectAddRequest, string reason) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null) return false; + + var muteGroupMemberEvent = GroupKickMemberEvent.Create(groupUin, uid, rejectAddRequest, reason); + var events = await Collection.Business.SendEvent(muteGroupMemberEvent); + return events.Count != 0 && ((GroupKickMemberEvent)events[0]).ResultCode == 0; + } + + public async Task SetGroupAdmin(uint groupUin, uint targetUin, bool isAdmin) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null) return false; + + var setGroupAdminEvent = GroupSetAdminEvent.Create(groupUin, uid, isAdmin); + var events = await Collection.Business.SendEvent(setGroupAdminEvent); + return events.Count != 0 && ((GroupSetAdminEvent)events[0]).ResultCode == 0; + } + + public async Task<(int, string?)> SetGroupTodo(uint groupUin, uint sequence) + { + var setGroupTodoEvent = GroupSetTodoEvent.Create(groupUin, sequence); + var events = await Collection.Business.SendEvent(setGroupTodoEvent); + + if (events.Count == 0) return (-1, "No Events"); + + var @event = (GroupSetTodoEvent)events[0]; + + return (@event.ResultCode, @event.ResultMessage); + } + + public async Task<(int, string?)> RemoveGroupTodo(uint groupUin) + { + var setGroupTodoEvent = GroupRemoveTodoEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(setGroupTodoEvent); + + if (events.Count == 0) return (-1, "No Event"); + + var @event = (GroupRemoveTodoEvent)events[0]; + + return (@event.ResultCode, @event.ResultMessage); + } + + public async Task<(int, string?)> FinishGroupTodo(uint groupUin) + { + var setGroupTodoEvent = GroupFinishTodoEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(setGroupTodoEvent); + + if (events.Count == 0) return (-1, "No Event"); + + var @event = (GroupFinishTodoEvent)events[0]; + + return (@event.ResultCode, @event.ResultMessage); + } + + public async Task GetGroupTodo(uint groupUin) + { + var setGroupTodoEvent = GroupGetTodoEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(setGroupTodoEvent); + + if (events.Count == 0) return new(-1, "No Event", 0, 0, string.Empty); + + var @event = (GroupGetTodoEvent)events[0]; + + return new( + @event.ResultCode, + @event.ResultMessage, + @event.GroupUin, + @event.Sequence, + @event.Preview + ); + } + + public async Task SetGroupBot(uint BotId, uint On, uint groupUin) + { + var muteBotEvent = GroupSetBotEvent.Create(BotId, On, groupUin); + var events = await Collection.Business.SendEvent(muteBotEvent); + return events.Count != 0 && ((GroupSetBotEvent)events[0]).ResultCode == 0; + } + + public async Task SetGroupBotHD(uint BotId, uint groupUin, string? data_1, string? data_2) + { + var muteBotEvent = GroupSetBothdEvent.Create(BotId, groupUin, data_1, data_2); + var events = await Collection.Business.SendEvent(muteBotEvent); + return events.Count != 0 && ((GroupSetBothdEvent)events[0]).ResultCode == 0; + } + + public async Task RenameGroupMember(uint groupUin, uint targetUin, string targetName) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null) return false; + + var renameGroupEvent = RenameMemberEvent.Create(groupUin, uid, targetName); + var events = await Collection.Business.SendEvent(renameGroupEvent); + return events.Count != 0 && ((RenameMemberEvent)events[0]).ResultCode == 0; + } + + public async Task RenameGroup(uint groupUin, string targetName) + { + var renameGroupEvent = GroupRenameEvent.Create(groupUin, targetName); + var events = await Collection.Business.SendEvent(renameGroupEvent); + return events.Count != 0 && ((GroupRenameEvent)events[0]).ResultCode == 0; + } + + public async Task RemarkGroup(uint groupUin, string targetRemark) + { + var renameGroupEvent = GroupRemarkEvent.Create(groupUin, targetRemark); + var events = await Collection.Business.SendEvent(renameGroupEvent); + return events.Count != 0 && ((GroupRemarkEvent)events[0]).ResultCode == 0; + } + + public async Task LeaveGroup(uint groupUin) + { + var leaveGroupEvent = GroupLeaveEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(leaveGroupEvent); + return events.Count != 0 && ((GroupLeaveEvent)events[0]).ResultCode == 0; + } + + public async Task FetchGroupFSSpace(uint groupUin) + { + var groupFSSpaceEvent = GroupFSSpaceEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(groupFSSpaceEvent); + return ((GroupFSSpaceEvent)events[0]).TotalSpace - ((GroupFSSpaceEvent)events[0]).UsedSpace; + } + + public async Task FetchGroupFSCount(uint groupUin) + { + var groupFSSpaceEvent = GroupFSCountEvent.Create(groupUin); + var events = await Collection.Business.SendEvent(groupFSSpaceEvent); + return ((GroupFSCountEvent)events[0]).FileCount; + } + + public async Task> FetchGroupFSList(uint groupUin, string targetDirectory) + { + uint startIndex = 0; + var entries = new List(); + while (true) + { + var groupFSListEvent = GroupFSListEvent.Create(groupUin, targetDirectory, startIndex, 20); + var events = await Collection.Business.SendEvent(groupFSListEvent); + if (events.Count == 0) break; + entries.AddRange(((GroupFSListEvent)events[0]).FileEntries); + if (((GroupFSListEvent)events[0]).IsEnd) break; + startIndex += 20; + } + + return entries; + } + + public async Task FetchGroupFSDownload(uint groupUin, string fileId) + { + var groupFSDownloadEvent = GroupFSDownloadEvent.Create(groupUin, fileId); + var events = await Collection.Business.SendEvent(groupFSDownloadEvent); + return $"{((GroupFSDownloadEvent)events[0]).FileUrl}{fileId}"; + } + + public async Task FetchPrivateFSDownload(string fileId, string fileHash, uint userId) + { + var uid = await Collection.Business.CachingLogic.ResolveUid(null, userId); + if (uid == null) return "false"; + var privateFSDownloadEvent = FileDownloadEvent.Create(fileId, fileHash, uid, uid); + var events = await Collection.Business.SendEvent(privateFSDownloadEvent); + return $"{((FileDownloadEvent)events[0]).FileUrl}"; + } + + public async Task<(int, string)> GroupFSMove(uint groupUin, string fileId, string parentDirectory, string targetDirectory) + { + var groupFSMoveEvent = GroupFSMoveEvent.Create(groupUin, fileId, parentDirectory, targetDirectory); + var events = await Collection.Business.SendEvent(groupFSMoveEvent); + int retCode = events.Count > 0 ? ((GroupFSMoveEvent)events[0]).ResultCode : -1; + string retMsg = events.Count > 0 ? ((GroupFSMoveEvent)events[0]).RetMsg : string.Empty; + return (retCode, retMsg); + } + + public async Task<(int, string)> GroupFSDelete(uint groupUin, string fileId) + { + var groupFSDeleteEvent = GroupFSDeleteEvent.Create(groupUin, fileId); + var events = await Collection.Business.SendEvent(groupFSDeleteEvent); + int retCode = events.Count > 0 ? ((GroupFSDeleteEvent)events[0]).ResultCode : -1; + string retMsg = events.Count > 0 ? ((GroupFSDeleteEvent)events[0]).RetMsg : string.Empty; + return (retCode, retMsg); + } + + public async Task<(int, string)> GroupFSCreateFolder(uint groupUin, string name) + { + var groupFSCreateFolderEvent = GroupFSCreateFolderEvent.Create(groupUin, name); + var events = await Collection.Business.SendEvent(groupFSCreateFolderEvent); + int retCode = events.Count > 0 ? ((GroupFSCreateFolderEvent)events[0]).ResultCode : -1; + string retMsg = events.Count > 0 ? ((GroupFSCreateFolderEvent)events[0]).RetMsg : string.Empty; + return (retCode, retMsg); + } + + public async Task<(int, string)> GroupFSDeleteFolder(uint groupUin, string folderId) + { + var groupFSDeleteFolderEvent = GroupFSDeleteFolderEvent.Create(groupUin, folderId); + var events = await Collection.Business.SendEvent(groupFSDeleteFolderEvent); + int retCode = events.Count > 0 ? ((GroupFSDeleteFolderEvent)events[0]).ResultCode : -1; + string retMsg = events.Count > 0 ? ((GroupFSDeleteFolderEvent)events[0]).RetMsg : string.Empty; + return (retCode, retMsg); + } + + public async Task<(int, string)> GroupFSRenameFolder(uint groupUin, string folderId, string newFolderName) + { + var groupFSDeleteFolderEvent = GroupFSRenameFolderEvent.Create(groupUin, folderId, newFolderName); + var events = await Collection.Business.SendEvent(groupFSDeleteFolderEvent); + int retCode = events.Count > 0 ? ((GroupFSRenameFolderEvent)events[0]).ResultCode : -1; + string retMsg = events.Count > 0 ? ((GroupFSRenameFolderEvent)events[0]).RetMsg : ""; + return (retCode, retMsg); + } + + public Task GroupFSUpload(uint groupUin, FileEntity fileEntity, string targetDirectory) + { + try + { + return FileUploader.UploadGroup(Collection, MessageBuilder.Group(groupUin).Build(), fileEntity, targetDirectory); + } + catch + { + return Task.FromResult(false); + } + } + + public async Task UploadFriendFile(uint targetUin, FileEntity fileEntity) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(null, targetUin); + var chain = new MessageChain(targetUin, Collection.Keystore.Uid ?? "", uid ?? "") { fileEntity }; + + try + { + return await FileUploader.UploadPrivate(Collection, chain, fileEntity); + } + catch + { + return false; + } + } + + public async Task RecallGroupMessage(uint groupUin, MessageResult result) + { + if (result.Sequence == null) return false; + + var recallMessageEvent = RecallGroupMessageEvent.Create(groupUin, result.Sequence.Value); + var events = await Collection.Business.SendEvent(recallMessageEvent); + return events.Count != 0 && ((RecallGroupMessageEvent)events[0]).ResultCode == 0; + } + + public async Task RecallGroupMessage(MessageChain chain) + { + if (chain.GroupUin == null) return false; + + var recallMessageEvent = RecallGroupMessageEvent.Create(chain.GroupUin.Value, chain.Sequence); + var events = await Collection.Business.SendEvent(recallMessageEvent); + return events.Count != 0 && ((RecallGroupMessageEvent)events[0]).ResultCode == 0; + } + + public async Task RecallGroupMessage(uint groupUin, uint sequence) + { + var recallMessageEvent = RecallGroupMessageEvent.Create(groupUin, sequence); + var events = await Collection.Business.SendEvent(recallMessageEvent); + return events.Count != 0 && ((RecallGroupMessageEvent)events[0]).ResultCode == 0; + } + + public async Task RecallFriendMessage(uint friendUin, MessageResult result) + { + if (result.Sequence == null) return false; + if (await Collection.Business.CachingLogic.ResolveUid(null, friendUin) is not { } uid) return false; + + var recallMessageEvent = RecallFriendMessageEvent.Create(uid, result.ClientSequence, result.Sequence ?? 0, (uint)(result.MessageId & uint.MaxValue), result.Timestamp); + var events = await Collection.Business.SendEvent(recallMessageEvent); + return events.Count != 0 && ((RecallFriendMessageEvent)events[0]).ResultCode == 0; + } + + public async Task RecallFriendMessage(MessageChain chain) + { + if (await Collection.Business.CachingLogic.ResolveUid(null, chain.TargetUin) is not { } uid) return false; + + uint timestamp = (uint)new DateTimeOffset(chain.Time).ToUnixTimeSeconds(); + var recallMessageEvent = RecallFriendMessageEvent.Create(uid, chain.ClientSequence, chain.Sequence, (uint)(chain.MessageId & uint.MaxValue), timestamp); + var events = await Collection.Business.SendEvent(recallMessageEvent); + return events.Count != 0 && ((RecallFriendMessageEvent)events[0]).ResultCode == 0; + } + + public async Task?> FetchGroupRequests() + { + var fetchRequestsEvent = FetchGroupRequestsEvent.Create(); + var events = await Collection.Business.SendEvent(fetchRequestsEvent); + if (events.Count == 0) return null; + + var resolved = events.Cast().SelectMany(e => e.Events).ToList(); + var results = new List(); + + foreach (var result in resolved) + { + var uins = await Task.WhenAll(ResolveUid(result.InvitorMemberUid), ResolveUid(result.TargetMemberUid), + ResolveUid(result.OperatorUid)); + uint invitorUin = uins[0]; + uint targetUin = uins[1]; + uint operatorUin = uins[2]; + + results.Add(new BotGroupRequest( + result.GroupUin, + invitorUin, + result.InvitorMemberCard, + targetUin, + result.TargetMemberCard, + operatorUin, + result.OperatorName, + result.State, + result.Sequence, + result.EventType, + result.Comment, + result.IsFiltered + )); + } + + return results; + + async Task ResolveUid(string? uid) + { + if (uid == null) return 0; + + var fetchUidEvent = FetchUserInfoEvent.Create(uid); + var e = await Collection.Business.SendEvent(fetchUidEvent); + return e.Count == 0 ? 0 : ((FetchUserInfoEvent)e[0]).UserInfo.Uin; + } + } + + public async Task?> FetchFriendRequests() + { + var fetchRequestsEvent = FetchFriendsRequestsEvent.Create(); + var events = await Collection.Business.SendEvent(fetchRequestsEvent); + if (events.Count == 0) return null; + + var resolved = ((FetchFriendsRequestsEvent)events[0]).Requests; + foreach (var result in resolved) + { + var uins = await Task.WhenAll(ResolveUid(result.TargetUid), ResolveUid(result.SourceUid)); + result.TargetUin = uins[0]; + result.SourceUin = uins[1]; + } + + return resolved; + + async Task ResolveUid(string? uid) + { + if (uid == null) return 0; + + var fetchUidEvent = FetchUserInfoEvent.Create(uid); + var e = await Collection.Business.SendEvent(fetchUidEvent); + return e.Count == 0 ? 0 : ((FetchUserInfoEvent)e[0]).UserInfo.Uin; + } + } + + public async Task GroupTransfer(uint groupUin, uint targetUin) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null || Collection.Keystore.Uid is not { } source) return false; + + var transferEvent = GroupTransferEvent.Create(groupUin, source, uid); + var results = await Collection.Business.SendEvent(transferEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task SetStatus(uint status) + { + var setStatusEvent = SetStatusEvent.Create(status, 0); + var results = await Collection.Business.SendEvent(setStatusEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task SetCustomStatus(uint faceId, string text) + { + var setCustomStatusEvent = SetCustomStatusEvent.Create(faceId, text); + var results = await Collection.Business.SendEvent(setCustomStatusEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task DeleteFriend(uint targetUin, bool block) + { + var uid = await Collection.Business.CachingLogic.ResolveUid(null, targetUin); + var deleteFriendEvent = DeleteFriendEvent.Create(uid, block); + var results = await Collection.Business.SendEvent(deleteFriendEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task RequestFriend(uint targetUin, string question, string message) + { + var requestFriendSearchEvent = RequestFriendSearchEvent.Create(targetUin); + var searchEvents = await Collection.Business.SendEvent(requestFriendSearchEvent); + if (searchEvents.Count == 0) return false; + await Task.Delay(5000); + + var requestFriendSettingEvent = RequestFriendSettingEvent.Create(targetUin); + var settingEvents = await Collection.Business.SendEvent(requestFriendSettingEvent); + if (settingEvents.Count == 0) return false; + + var requestFriendEvent = RequestFriendEvent.Create(targetUin, message, question); + var events = await Collection.Business.SendEvent(requestFriendEvent); + return events.Count != 0 && ((RequestFriendEvent)events[0]).ResultCode == 0; + } + + public async Task Like(uint targetUin, uint count) + { + var uid = await Collection.Business.CachingLogic.ResolveUid(null, targetUin); + if (uid == null) return false; + + var friendLikeEvent = FriendLikeEvent.Create(uid, count); + var results = await Collection.Business.SendEvent(friendLikeEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task InviteGroup(uint targetGroupUin, Dictionary invitedUins) + { + var invitedUids = new Dictionary(invitedUins.Count); + foreach (var (friendUin, groupUin) in invitedUins) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, friendUin); + if (uid != null) invitedUids[uid] = groupUin; + } + + var @event = GroupInviteEvent.Create(targetGroupUin, invitedUids); + var results = await Collection.Business.SendEvent(@event); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task GetClientKey() + { + var clientKeyEvent = FetchClientKeyEvent.Create(); + var events = await Collection.Business.SendEvent(clientKeyEvent); + return events.Count == 0 ? null : ((FetchClientKeyEvent)events[0]).ClientKey; + } + + public async Task SetGroupRequest(uint groupUin, ulong sequence, uint type, bool accept, string reason) + { + var inviteEvent = SetGroupRequestEvent.Create(accept, groupUin, sequence, type, reason); + var results = await Collection.Business.SendEvent(inviteEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task SetGroupFilteredRequest(uint groupUin, ulong sequence, uint type, bool accept, string reason) + { + var inviteEvent = SetGroupFilteredRequestEvent.Create(accept, groupUin, sequence, type, reason); + var results = await Collection.Business.SendEvent(inviteEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task SetFriendRequest(string targetUid, bool accept) + { + var inviteEvent = SetFriendRequestEvent.Create(targetUid, accept); + var results = await Collection.Business.SendEvent(inviteEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task?> GetGroupMessage(uint groupUin, uint startSequence, uint endSequence) + { + var getMsgEvent = GetGroupMessageEvent.Create(groupUin, startSequence, endSequence); + var results = await Collection.Business.SendEvent(getMsgEvent); + return results.Count != 0 ? ((GetGroupMessageEvent)results[0]).Chains : null; + } + + public async Task?> GetRoamMessage(uint friendUin, uint time, uint count) + { + if (await Collection.Business.CachingLogic.ResolveUid(null, friendUin) is not { } uid) return null; + + var roamEvent = GetRoamMessageEvent.Create(uid, time, count); + var results = await Collection.Business.SendEvent(roamEvent); + return results.Count != 0 ? ((GetRoamMessageEvent)results[0]).Chains : null; + } + + public async Task?> GetC2cMessage(uint friendUin, uint startSequence, uint endSequence) + { + if (await Collection.Business.CachingLogic.ResolveUid(null, friendUin) is not { } uid) return null; + + var c2cEvent = GetC2cMessageEvent.Create(uid, startSequence, endSequence); + var results = await Collection.Business.SendEvent(c2cEvent); + return results.Count != 0 ? ((GetC2cMessageEvent)results[0]).Chains : null; + } + + public async Task<(int code, List? chains)> GetMessagesByResId(string resId) + { + var @event = MultiMsgDownloadEvent.Create(Collection.Keystore.Uid ?? "", resId); + var results = await Collection.Business.SendEvent(@event); + + if (results.Count == 0) return (-9999, null); + var result = (MultiMsgDownloadEvent)results[0]; + + return (result.ResultCode, result.Chains); + } + + public async Task?> FetchCustomFace() + { + var fetchCustomFaceEvent = FetchCustomFaceEvent.Create(); + var results = await Collection.Business.SendEvent(fetchCustomFaceEvent); + return results.Count != 0 ? ((FetchCustomFaceEvent)results[0]).Urls : null; + } + + public async Task UploadLongMessage(List chains) + { + var multiMsgUploadEvent = MultiMsgUploadEvent.Create(null, chains); + var results = await Collection.Business.SendEvent(multiMsgUploadEvent); + return results.Count != 0 ? ((MultiMsgUploadEvent)results[0]).ResId : null; + } + + public async Task MarkAsRead(uint groupUin, string? targetUid, uint startSequence, uint time) + { + var markAsReadEvent = MarkReadedEvent.Create(groupUin, targetUid, startSequence, time); + var results = await Collection.Business.SendEvent(markAsReadEvent); + return results.Count != 0 && ((MarkReadedEvent)results[0]).ResultCode == 0; + } + + public async Task FriendPoke(uint friendUin) + { + var friendPokeEvent = FriendPokeEvent.Create(friendUin); + var results = await Collection.Business.SendEvent(friendPokeEvent); + return results.Count != 0 && ((FriendPokeEvent)results[0]).ResultCode == 0; + } + + public async Task GroupPoke(uint groupUin, uint friendUin) + { + var friendPokeEvent = GroupPokeEvent.Create(friendUin, groupUin); + var results = await Collection.Business.SendEvent(friendPokeEvent); + return results.Count != 0 && ((FriendPokeEvent)results[0]).ResultCode == 0; + } + + public async Task SetEssenceMessage(uint groupUin, uint sequence, uint random) + { + var setEssenceMessageEvent = SetEssenceMessageEvent.Create(groupUin, sequence, random); + var results = await Collection.Business.SendEvent(setEssenceMessageEvent); + return results.Count != 0 && ((SetEssenceMessageEvent)results[0]).ResultCode == 0; + } + + public async Task RemoveEssenceMessage(uint groupUin, uint sequence, uint random) + { + var removeEssenceMessageEvent = RemoveEssenceMessageEvent.Create(groupUin, sequence, random); + var results = await Collection.Business.SendEvent(removeEssenceMessageEvent); + return results.Count != 0 && ((RemoveEssenceMessageEvent)results[0]).ResultCode == 0; + } + + public async Task GroupSetSpecialTitle(uint groupUin, uint targetUin, string title) + { + string? uid = await Collection.Business.CachingLogic.ResolveUid(groupUin, targetUin); + if (uid == null) return false; + + var groupSetSpecialTitleEvent = GroupSetSpecialTitleEvent.Create(groupUin, uid, title); + var events = await Collection.Business.SendEvent(groupSetSpecialTitleEvent); + return events.Count != 0 && ((GroupSetSpecialTitleEvent)events[0]).ResultCode == 0; + } + + public async Task FetchUserInfo(uint uin, bool refreshCache = false) + { + return await Collection.Business.CachingLogic.GetCachedUsers(uin, refreshCache); + } + + public async Task<(int code, string? message, BotGroupInfo info)> FetchGroupInfo(ulong uin) + { + var events = await Collection.Business.SendEvent(GetGroupInfoEvent.Create(uin)); + if (events.Count == 0) return (-1, "No Result", new()); + var @event = (GetGroupInfoEvent)events[0]; + return (@event.ResultCode, @event.Message, @event.Info); + } + + public async Task SetMessageReaction(uint groupUin, uint sequence, string code, bool isAdd) + { + if (isAdd) + { + var addReactionEvent = GroupAddReactionEvent.Create(groupUin, sequence, code); + var results = await Collection.Business.SendEvent(addReactionEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + else + { + var reduceReactionEvent = GroupReduceReactionEvent.Create(groupUin, sequence, code); + var results = await Collection.Business.SendEvent(reduceReactionEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + } + + public async Task SetNeedToConfirmSwitch(bool enableNoNeed) + { + var setNeedToConfirmSwitchEvent = SetNeedToConfirmSwitchEvent.Create(enableNoNeed); + var results = await Collection.Business.SendEvent(setNeedToConfirmSwitchEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task?> FetchMarketFaceKey(List faceIds) + { + var fetchMarketFaceKeyEvent = FetchMarketFaceKeyEvent.Create(faceIds); + var results = await Collection.Business.SendEvent(fetchMarketFaceKeyEvent); + return results.Count != 0 ? ((FetchMarketFaceKeyEvent)results[0]).Keys : null; + } + + public async Task GroupClockIn(uint groupUin) + { + var groupClockInEvent = GroupClockInEvent.Create(groupUin); + var results = await Collection.Business.SendEvent(groupClockInEvent); + return ((GroupClockInEvent)results[0]).ResultInfo ?? new BotGroupClockInResult(false); + } + + public Task FriendSpecialShake(uint friendUin, SpecialPokeFaceType type, uint count) + { + var chain = MessageBuilder.Friend(friendUin) + .SpecialPoke(type, count) + .Build(); + return SendMessage(chain); + } + + public Task FriendShake(uint friendUin, PokeFaceType type, uint strength) + { + var chain = MessageBuilder.Friend(friendUin) + .Poke(type, strength) + .Build(); + return SendMessage(chain); + } + + public async Task SetAvatar(ImageEntity avatar) + { + if (avatar.ImageStream == null) return false; + + var highwayUrlEvent = HighwayUrlEvent.Create(); + var highwayUrlResults = await Collection.Business.SendEvent(highwayUrlEvent); + if (highwayUrlResults.Count == 0) return false; + + var ticket = ((HighwayUrlEvent)highwayUrlResults[0]).SigSession; + var md5 = avatar.ImageStream.Value.Md5().UnHex(); + return await Collection.Highway.UploadSrcByStreamAsync(90, avatar.ImageStream.Value, ticket, md5, + Array.Empty()); + } + + public async Task GroupSetAvatar(uint groupUin, ImageEntity avatar) + { + if (avatar.ImageStream == null) return false; + + var highwayUrlEvent = HighwayUrlEvent.Create(); + var highwayUrlResults = await Collection.Business.SendEvent(highwayUrlEvent); + if (highwayUrlResults.Count == 0) return false; + + var ticket = ((HighwayUrlEvent)highwayUrlResults[0]).SigSession; + var md5 = avatar.ImageStream.Value.Md5().UnHex(); + var extra = new GroupAvatarExtra + { + Type = 101, + GroupUin = groupUin, + Field3 = new GroupAvatarExtraField3 { Field1 = 1 }, + Field5 = 3, + Field6 = 1 + }.Serialize().ToArray(); + return await Collection.Highway.UploadSrcByStreamAsync(3000, avatar.ImageStream.Value, ticket, md5, extra); + } + + public async Task<(uint, uint)> GroupRemainAtAll(uint groupUin) + { + var groupRemainAtAllEvent = FetchGroupAtAllRemainEvent.Create(groupUin); + var results = await Collection.Business.SendEvent(groupRemainAtAllEvent); + if (results.Count == 0) return (0, 0); + + var ret = (FetchGroupAtAllRemainEvent)results[0]; + return (ret.RemainAtAllCountForUin, ret.RemainAtAllCountForGroup); + } + + public async Task FetchSuperFaceId(uint id) => + await Collection.Business.CachingLogic.GetCachedIsSuperFaceId(id); + + public async Task FetchFaceEntity(uint id) => + await Collection.Business.CachingLogic.GetCachedFaceEntity(id); + + public async Task GroupJoinEmojiChain(uint groupUin, uint emojiId, uint targetMessageSeq) + { + var groupJoinEmojiChainEvent = GroupJoinEmojiChainEvent.Create(targetMessageSeq, emojiId, groupUin); + var results = await Collection.Business.SendEvent(groupJoinEmojiChainEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task FriendJoinEmojiChain(uint friendUin, uint emojiId, uint targetMessageSeq) + { + string? friendUid = await Collection.Business.CachingLogic.ResolveUid(null, friendUin); + if (friendUid == null) return false; + var friendJoinEmojiChainEvent = FriendJoinEmojiChainEvent.Create(targetMessageSeq, emojiId, friendUid); + var results = await Collection.Business.SendEvent(friendJoinEmojiChainEvent); + return results.Count != 0 && results[0].ResultCode == 0; + } + + public async Task<(int Code, string ErrMsg, string? Url)> GetGroupGenerateAiRecordUrl(uint groupUin, string character, string text, uint chatType) + { + var (code, errMsg, record) = await GetGroupGenerateAiRecord(groupUin, character, text, chatType); + if (code != 0) + return (code, errMsg, null); + + var recordGroupDownloadEvent = RecordGroupDownloadEvent.Create(groupUin, record!.MsgInfo!); + var @event = await Collection.Business.SendEvent(recordGroupDownloadEvent); + if (@event.Count == 0) return (-1, "running event missing!", null); + + var finalResult = (RecordGroupDownloadEvent)@event[0]; + return finalResult.ResultCode == 0 + ? (finalResult.ResultCode, string.Empty, finalResult.AudioUrl) + : (finalResult.ResultCode, "Failed to get group ai record", null); + } + + public async Task<(int Code, string ErrMsg, RecordEntity? Record)> GetGroupGenerateAiRecord(uint groupUin, string character, string text, uint chatType) + { + var groupAiRecordEvent = GroupAiRecordEvent.Create(groupUin, character, text, chatType, (uint)Random.Shared.Next()); + while (true) + { + var results = await Collection.Business.SendEvent(groupAiRecordEvent); + if (results.Count == 0) return (-1, "running event missing!", null); + var aiRecordResult = (GroupAiRecordEvent)results[0]; + if (aiRecordResult.ResultCode != 0) + return (aiRecordResult.ResultCode, aiRecordResult.ErrorMessage, null); + if (aiRecordResult.RecordInfo is not null) + { + var index = aiRecordResult.RecordInfo!.MsgInfoBody[0].Index; + return (aiRecordResult.ResultCode, string.Empty, new RecordEntity(index.FileUuid, index.Info.FileName, index.Info.FileHash.UnHex()) + { + AudioLength = (int)index.Info.Time, + FileSha1 = index.Info.FileSha1, + MsgInfo = aiRecordResult.RecordInfo + }); + } + } + + } + + public async Task<(int Code, string ErrMsg, List? Result)> GetAiCharacters(uint chatType, uint groupUin) + { + var fetchAiRecordListEvent = FetchAiCharacterListEvent.Create(chatType, groupUin); + + var results = await Collection.Business.SendEvent(fetchAiRecordListEvent); + if (results.Count == 0) return (-1, "Event missing!", null); + + var result = (FetchAiCharacterListEvent)results[0]; + + return (result.ResultCode, result.ErrorMessage, result.AiCharacters); + } + + public async Task UploadImage(ImageEntity image) + { + await Collection.Highway.ManualUploadEntity(image); + var msgInfo = image.MsgInfo; + if (msgInfo is null) throw new Exception(); + var downloadEvent = ImageDownloadEvent.Create(Collection.Keystore.Uid ?? "", msgInfo); + var result = await Collection.Business.SendEvent(downloadEvent); + var ret = (ImageDownloadEvent)result[0]; + return ret.ImageUrl; + } + + public async Task ImageOcr(string imageUrl) + { + var imageOcrEvent = ImageOcrEvent.Create(imageUrl); + var results = await Collection.Business.SendEvent(imageOcrEvent); + return results.Count != 0 ? ((ImageOcrEvent)results[0]).ImageOcrResult : null; + } + + public async Task ImageOcr(ImageEntity image) + { + var imageUrl = await UploadImage(image); + return await ImageOcr(imageUrl); + } + + public async Task<(int Retcode, string Message, List FriendUins, List GroupUins)> GetPins() + { + var @event = FetchPinsEvent.Create(); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count == 0) + { + return (-1, "No Result", new(), new()); + } + + var result = (FetchPinsEvent)results[0]; + return (result.ResultCode, result.Message, result.FriendUins, result.GroupUins); + } + + public async Task<(int Retcode, string Message)> SetPinFriend(uint uin, bool isPin) + { + var @event = SetPinFriendEvent.Create(uin, isPin); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count == 0) + { + return (-1, "No Result"); + } + + var result = (SetPinFriendEvent)results[0]; + return (result.ResultCode, result.Message); + } + + public async Task<(int Retcode, string Message)> SetPinGroup(uint uin, bool isPin) + { + var @event = SetPinGroupEvent.Create(uin, isPin); + + var results = await Collection.Business.SendEvent(@event); + if (results.Count == 0) + { + return (-1, "No Result"); + } + + var result = (SetPinGroupEvent)results[0]; + return (result.ResultCode, result.Message); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/WtExchangeLogic.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/WtExchangeLogic.cs new file mode 100644 index 0000000..dc35e84 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/Implementation/WtExchangeLogic.cs @@ -0,0 +1,532 @@ +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; +using System.Web; +using Lagrange.Core.Common; +using Lagrange.Core.Event.EventArg; +using Lagrange.Core.Internal.Context.Attributes; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Login.NTLogin; +using Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; +using Lagrange.Core.Internal.Service; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Network; + +// ReSharper disable AsyncVoidLambda + +namespace Lagrange.Core.Internal.Context.Logic.Implementation; + +[EventSubscribe(typeof(TransEmpEvent))] +[EventSubscribe(typeof(LoginEvent))] +[EventSubscribe(typeof(KickNTEvent))] +[BusinessLogic("WtExchangeLogic", "Manage the online task of the Bot")] +internal class WtExchangeLogic : LogicBase +{ + private const string Tag = nameof(WtExchangeLogic); + + private readonly Timer _reLoginTimer; + + private TaskCompletionSource _transEmpTask = new(); + private TaskCompletionSource<(string, string)> _captchaTask = new(); + + private const string Interface = "https://ntlogin.qq.com/qr/getFace"; + + private const string QueryEvent = "wtlogin.trans_emp CMD0x12"; + private const string HeartbeatEvent = "Heartbeat.Alive"; + private const string SsoHeartbeatEvent = "SsoHeartBeat"; + + internal WtExchangeLogic(ContextCollection collection) : base(collection) + { + _reLoginTimer = new Timer(async _ => await ReLogin(), null, Timeout.Infinite, Timeout.Infinite); + } + + public override async Task Incoming(ProtocolEvent e) + { + switch (e) + { + case KickNTEvent kick: + Collection.Log.LogFatal(Tag, $"KickNTEvent: {kick.Tag}: {kick.Message}"); + Collection.Log.LogFatal(Tag, "Bot will be offline in 5 seconds..."); + await Task.Delay(5000); + + Collection.Invoker.PostEvent(new BotOfflineEvent(kick.Tag, kick.Message)); // TODO: Fill in the reason of offline + Collection.Scheduler.Dispose(); + break; + } + } + + private void Reset() + { + _transEmpTask = new TaskCompletionSource(); + _captchaTask = new TaskCompletionSource<(string, string)>(); + } + + private void OnCancellation() + { + Collection.Scheduler.Cancel(QueryEvent); + Collection.Scheduler.Cancel(HeartbeatEvent); + _transEmpTask.TrySetException(new TaskCanceledException()); + _captchaTask.TrySetException(new TaskCanceledException()); + } + + /// + /// 1. resolve wtlogin.trans_emp CMD0x31 packet + /// 2. Schedule wtlogin.trans_emp CMD0x12 Task + /// + public async Task<(string, byte[])?> FetchQrCode() + { + Collection.Log.LogInfo(Tag, "Connecting Servers..."); + if (!await Collection.Socket.Connect()) return null; + Collection.Scheduler.Interval(HeartbeatEvent, 10 * 1000, async () => await Collection.Business.PushEvent(AliveEvent.Create())); + + if (Collection.Keystore.Session.D2.Length != 0) + { + Collection.Log.LogWarning(Tag, "Invalid Session found, try to clean D2Key, D2 and TGT Token"); + Collection.Keystore.ClearSession(); + } + + var transEmp = TransEmpEvent.Create(TransEmpEvent.State.FetchQrCode); + var result = await Collection.Business.SendEvent(transEmp); + + if (result.Count != 0) + { + var @event = (TransEmpEvent)result[0]; + Collection.Keystore.Session.QrString = @event.QrSig; + Collection.Keystore.Session.QrSign = @event.Signature; + Collection.Keystore.Session.QrUrl = @event.Url; + + Collection.Log.LogInfo(Tag, $"QrCode Fetched, Expiration: {@event.Expiration} seconds"); + return (@event.Url, @event.QrCode); + } + return null; + } + + public Task LoginByQrCode(CancellationToken cancellationToken) + { + Reset(); + cancellationToken.Register(OnCancellation); + + Collection.Scheduler.Interval(QueryEvent, 2 * 1000, async () => await QueryTransEmpState(async @event => + { + if (@event.TgtgtKey != null) + { + Collection.Keystore.Stub.TgtgtKey = @event.TgtgtKey; + Collection.Keystore.Session.TempPassword = @event.TempPassword; + Collection.Keystore.Session.NoPicSig = @event.NoPicSig; + } + + return await DoWtLogin(); + })); + + return _transEmpTask.Task; + } + + public Task LoginByPassword(CancellationToken token) => LoginByEasy(true, token); + + public async Task LoginByEasy(bool easyFallbackToPassword, CancellationToken cancellationToken) + { + Reset(); + cancellationToken.Register(OnCancellation); + + if (!Collection.Socket.Connected) // if socket not connected, try to connect + { + if (!await Collection.Socket.Connect()) return false; + Collection.Scheduler.Interval(HeartbeatEvent, 10 * 1000, async () => await Collection.Business.PushEvent(AliveEvent.Create())); + } + + if (Collection.Keystore.Session.D2.Length > 0 && Collection.Keystore.Session.Tgt.Length > 0 && + DateTime.Now - Collection.Keystore.Session.SessionDate < TimeSpan.FromDays(15)) + { + Collection.Log.LogInfo(Tag, "Session has not expired, using session to login and register status"); + try + { + if (await BotOnline()) return true; + + Collection.Log.LogWarning(Tag, "Register by session failed, try to login by EasyLogin"); + } + catch + { + Collection.Log.LogWarning(Tag, "Register by session failed, try to login by EasyLogin"); + } + } + + if (Collection.Keystore.Session.ExchangeKey == null) + { + Collection.Keystore.ClearSession(); + + if (!await KeyExchange()) + { + Collection.Log.LogInfo(Tag, "Key Exchange Failed, please try again later"); + return false; + } + } + + if (Collection.Keystore.Session.TempPassword != null) // try EasyLogin + { + Collection.Log.LogInfo(Tag, "Trying to Login by EasyLogin..."); + var easyLoginEvent = EasyLoginEvent.Create(); + var easyLoginResult = await Collection.Business.SendEvent(easyLoginEvent); + + if (easyLoginResult.Count != 0) + { + switch ((LoginCommon.Error)easyLoginResult[0].ResultCode) + { + case LoginCommon.Error.Success: + { + Collection.Log.LogInfo(Tag, "Login Success, try to register services"); + return await BotOnline(); + } + case LoginCommon.Error.UnusualVerify: + { + Collection.Log.LogInfo(Tag, "Verification needed"); + + if (!await FetchUnusual()) + { + Collection.Log.LogInfo(Tag, "Fetch unusual state failed"); + return false; + } + + Collection.Scheduler.Interval(QueryEvent, 2 * 1000, async () => await QueryTransEmpState(async e => + { + if (e.TempPassword != null) + { + Collection.Keystore.Session.TempPassword = e.TempPassword; + return await DoUnusualEasyLogin(); + } + + return false; + })); + bool result = await _transEmpTask.Task; + return result && await BotOnline(); + } + default: + { + Collection.Log.LogWarning(Tag, $"Fast Login Failed with code {easyLoginResult[0].ResultCode}, trying to Login by Password..."); + + Collection.Keystore.Session.TempPassword = null; // clear temp password + return easyFallbackToPassword && await LoginByPassword(cancellationToken); // fallback to password + } + } + } + } + else if (easyFallbackToPassword) + { + Collection.Log.LogInfo(Tag, "Trying to Login by Password..."); + var passwordLoginEvent = PasswordLoginEvent.Create(); + var passwordLoginResult = await Collection.Business.SendEvent(passwordLoginEvent); + + if (passwordLoginResult.Count != 0) + { + var @event = (PasswordLoginEvent)passwordLoginResult[0]; + switch ((LoginCommon.Error)@event.ResultCode) + { + case LoginCommon.Error.Success: + { + Collection.Log.LogInfo(Tag, "Login Success"); + + await BotOnline(); + return true; + } + case LoginCommon.Error.UnusualVerify: + { + Collection.Log.LogInfo(Tag, "Unusual Verify is not currently supported for PasswordLogin"); + return false; + } + case LoginCommon.Error.CaptchaVerify: + { + Collection.Log.LogInfo(Tag, "Login captcha is required, please follow the link from event"); + + if (Collection.Keystore.Session.CaptchaUrl != null) + { + var captchaEvent = new BotCaptchaEvent(Collection.Keystore.Session.CaptchaUrl); + Collection.Invoker.PostEvent(captchaEvent); + + string aid = Collection.Keystore.Session.CaptchaUrl.Split("&sid=")[1].Split("&")[0]; + var (ticket, randStr) = await _captchaTask.Task; + Collection.Keystore.Session.Captcha = new ValueTuple(ticket, randStr, aid); + + return await LoginByPassword(cancellationToken); + } + + Collection.Log.LogInfo(Tag, "Captcha Url is null, please try again later"); + return false; + } + case LoginCommon.Error.NewDeviceVerify: + { + Collection.Log.LogInfo(Tag, $"NewDeviceVerify required, please notice the {nameof(BotNewDeviceVerifyEvent)} and encode into QRCode"); + string? parameters = Collection.Keystore.Session.NewDeviceVerifyUrl; + if (parameters == null) return false; + var parsed = HttpUtility.ParseQueryString(parameters); + + uint uin = Collection.Keystore.Uin; + string url = $"https://oidb.tim.qq.com/v3/oidbinterface/oidb_0xc9e_8?uid={uin}&getqrcode=1&sdkappid=39998&actype=2"; + var request = new NTNewDeviceQrCodeRequest + { + StrDevAuthToken = parsed["sig"] ?? "", + Uint32Flag = 1, + Uint32UrlType = 0, + StrUinToken = parsed["uin-token"] ?? "", + StrDevType = Collection.AppInfo.Os, + StrDevName = Collection.Device.DeviceName + }; + + var client = new HttpClient(); + var response = await client.PostAsJsonAsync(url, request, cancellationToken); + var json = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); + if (json == null) return false; + + var newDeviceEvent = new BotNewDeviceVerifyEvent(json.StrUrl, Array.Empty()); + Collection.Invoker.PostEvent(newDeviceEvent); + Collection.Log.LogInfo(Tag, $"NewDeviceLogin Url: {json.StrUrl}"); + + string? original = HttpUtility.ParseQueryString(json.StrUrl.Split("?")[1])["str_url"]; + if (original == null) return false; + + Collection.Scheduler.Interval(QueryEvent, 2 * 1000, async () => + { + var query = new NTNewDeviceQrCodeQuery + { + Uint32Flag = 0, + Token = Convert.ToBase64String(Encoding.UTF8.GetBytes(original)) + }; + var resp = await client.PostAsJsonAsync(url, query, cancellationToken); + var responseJson = await resp.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); + if (!string.IsNullOrEmpty(responseJson?.StrNtSuccToken)) + { + Collection.Scheduler.Cancel(QueryEvent); // cancel the event + + Collection.Keystore.Session.TempPassword = Encoding.UTF8.GetBytes(responseJson.StrNtSuccToken); + _transEmpTask.SetResult(true); + client.Dispose(); + } + else + { + Collection.Log.LogInfo(Tag, "NewDeviceLogin is waiting for scanning"); + } + }); + + if (await _transEmpTask.Task) + { + Collection.Log.LogInfo(Tag, "Trying to Login by NewDeviceLogin..."); + var newDeviceLogin = NewDeviceLoginEvent.Create(); + _ = await Collection.Business.SendEvent(newDeviceLogin); + return await BotOnline(); + } + + return false; + } + default: + { + Collection.Log.LogWarning(Tag, @event is { Message: not null, Tag: not null } + ? $"Login Failed: {(LoginCommon.Error)@event.ResultCode} | {@event.Tag}: {@event.Message}" + : $"Login Failed: {(LoginCommon.Error)@event.ResultCode}"); + + return false; + } + } + } + } + + return false; + } + + private async Task KeyExchange() + { + var keyExchangeEvent = KeyExchangeEvent.Create(); + var exchangeResult = await Collection.Business.SendEvent(keyExchangeEvent); + if (exchangeResult.Count != 0) + { + Collection.Log.LogInfo(Tag, "Key Exchange successfully!"); + return true; + } + + return false; + } + + private async Task DoWtLogin() + { + Collection.Log.LogInfo(Tag, "Doing Login..."); + Collection.Keystore.Session.Sequence = 0; + + Collection.Keystore.SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + var loginEvent = LoginEvent.Create(); + var result = await Collection.Business.SendEvent(loginEvent); + + if (result.Count != 0) + { + var @event = (LoginEvent)result[0]; + if (@event.ResultCode == 0) + { + Collection.Log.LogInfo(Tag, "Login Success"); + Collection.Keystore.Info = new BotKeystore.BotInfo(@event.Age, @event.Sex, @event.Name); + Collection.Log.LogInfo(Tag, Collection.Keystore.Info.ToString()); + return await BotOnline(); + } + + Collection.Log.LogFatal(Tag, $"Login failed: {@event.ResultCode}"); + Collection.Log.LogFatal(Tag, $"Tag: {@event.Tag}\nState: {@event.Message}"); + } + + return false; + } + + private async Task QueryTransEmpState(Func> callback) + { + if (Collection.Keystore.Session.QrString != null) + { + var request = new NTLoginHttpRequest + { + Appid = Collection.AppInfo.AppId, + Qrsig = Collection.Keystore.Session.QrString, + FaceUpdateTime = 0 + }; + + var payload = JsonSerializer.SerializeToUtf8Bytes(request); + var response = await Http.PostAsync(Interface, payload, "application/json"); + var info = JsonSerializer.Deserialize(response); + if (info != null) Collection.Keystore.Uin = info.Uin; + } + + var transEmp = TransEmpEvent.Create(TransEmpEvent.State.QueryResult); + var result = await Collection.Business.SendEvent(transEmp); + + if (result.Count != 0) + { + var @event = (TransEmpEvent)result[0]; + var state = (TransEmp12.State)@event.ResultCode; + Collection.Log.LogInfo(Tag, $"QrCode State Queried: {state} Uin: {Collection.Keystore.Uin}"); + + switch (state) + { + case TransEmp12.State.Confirmed: + { + Collection.Log.LogInfo(Tag, "QrCode Confirmed, Logging in with A1 sig..."); + Collection.Scheduler.Cancel(QueryEvent); // cancel query task + _transEmpTask.SetResult(await callback.Invoke(@event)); + break; + } + case TransEmp12.State.CodeExpired: + { + Collection.Log.LogWarning(Tag, "QrCode Expired, Please Fetch QrCode Again"); + Collection.Scheduler.Cancel(QueryEvent); + + _transEmpTask.SetResult(false); + return; + } + case TransEmp12.State.Canceled: + { + Collection.Log.LogWarning(Tag, "QrCode Canceled, Please Fetch QrCode Again"); + Collection.Scheduler.Cancel(QueryEvent); + + _transEmpTask.SetResult(false); + return; + } + case TransEmp12.State.WaitingForConfirm: + case TransEmp12.State.WaitingForScan: + default: + break; + } + } + + } + + public async Task BotOnline(BotOnlineEvent.OnlineReason reason = BotOnlineEvent.OnlineReason.Login) + { + var registerEvent = InfoSyncEvent.Create(); + var registerResponse = await Collection.Business.SendEvent(registerEvent); + var heartbeatDelegate = new Action(async () => await Collection.Business.PushEvent(SsoAliveEvent.Create())); + + if (registerResponse.Count != 0) + { + var resp = (InfoSyncEvent)registerResponse[0]; + Collection.Log.LogInfo(Tag, $"Register Status: {resp.Message}"); + + bool result = resp.Message.Contains("register success"); + if (result) + { + Collection.Scheduler.Interval(SsoHeartbeatEvent, (int)(4.5 * 60 * 1000), heartbeatDelegate); + + var onlineEvent = new BotOnlineEvent(reason); + Collection.Invoker.PostEvent(onlineEvent); + + _reLoginTimer.Change(TimeSpan.FromDays(15), TimeSpan.FromDays(15)); + Collection.Log.LogInfo(Tag, "AutoReLogin Enabled, session would be refreshed in 15 days period"); + } + + return result; + } + + return false; + } + + private async Task FetchUnusual() + { + var transEmp = TransEmpEvent.Create(TransEmpEvent.State.FetchQrCode); + var result = await Collection.Business.SendEvent(transEmp); + + if (result.Count != 0) + { + Collection.Log.LogInfo(Tag, "Confirmation Request Send"); + return true; + } + + return false; + } + + private async Task DoUnusualEasyLogin() + { + Collection.Log.LogInfo(Tag, "Trying to Login by EasyLogin..."); + var unusualEvent = UnusualEasyLoginEvent.Create(); + var result = await Collection.Business.SendEvent(unusualEvent); + return result.Count != 0 && ((UnusualEasyLoginEvent)result[0]).Success; + } + + private async Task ReLogin() + { + Collection.Log.LogInfo(Tag, "Session is about to expire, try to relogin and refresh"); + if (Collection.Keystore.Session.TempPassword == null) + { + Collection.Log.LogInfo(Tag, "A2 is null, abort"); + return; + } + + var d2 = Collection.Keystore.Session.D2; + var d2Key = Collection.Keystore.Session.D2Key; + var tgt = Collection.Keystore.Session.Tgt; // save the original state + + Collection.Socket.Disconnect(); + Collection.Keystore.ClearSession(); + await Collection.Socket.Connect(); + + if (await KeyExchange()) + { + var easyLoginEvent = EasyLoginEvent.Create(); + var easyLoginResult = await Collection.Business.SendEvent(easyLoginEvent); + if (easyLoginResult.Count != 0) + { + var result = (EasyLoginEvent)easyLoginResult[0]; + if ((LoginCommon.Error)result.ResultCode == LoginCommon.Error.Success) + { + Collection.Log.LogInfo(Tag, "Login Success, try to register services"); + if (await BotOnline(BotOnlineEvent.OnlineReason.Reconnect)) return; + + Collection.Log.LogInfo(Tag, "Re-login failed, please refresh manually"); + } + } + } + else + { + Collection.Log.LogInfo(Tag, "Key Exchange Failed, trying to online, please refresh manually"); + } + + Collection.Keystore.Session.D2 = d2; + Collection.Keystore.Session.D2Key = d2Key; + Collection.Keystore.Session.Tgt = tgt; + + await BotOnline(BotOnlineEvent.OnlineReason.Reconnect); + } + + public bool SubmitCaptcha(string ticket, string randStr) => _captchaTask.TrySetResult((ticket, randStr)); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/LogicBase.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/LogicBase.cs new file mode 100644 index 0000000..49a0458 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Logic/LogicBase.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Internal.Event; + +namespace Lagrange.Core.Internal.Context.Logic; + +internal abstract class LogicBase +{ + protected readonly ContextCollection Collection; + + protected LogicBase(ContextCollection collection) => Collection = collection; + + public virtual Task Incoming(ProtocolEvent e) => Task.CompletedTask; + + public virtual Task Outgoing(ProtocolEvent e) => Task.CompletedTask; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/PacketContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/PacketContext.cs new file mode 100644 index 0000000..927625a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/PacketContext.cs @@ -0,0 +1,110 @@ +using System.Collections.Concurrent; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Sign; + +#pragma warning disable CS4014 + +namespace Lagrange.Core.Internal.Context; + +/// +/// Translate the protocol event into SSOPacket and further ServiceMessage +/// And Dispatch the packet from by managing the sequence from Tencent's server +/// Every Packet should be sent and received from this context instead of being directly send to +/// +internal class PacketContext : ContextBase +{ + internal SignProvider SignProvider { private get; set; } + + private readonly ConcurrentDictionary> _pendingTasks; + + public PacketContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, BotConfig config) + : base(collection, keystore, appInfo, device) + { + SignProvider = config.CustomSignProvider ?? appInfo.Os switch + { + "Windows" => new WindowsSigner(), + "Mac" => new MacSigner(), + "Linux" => new LinuxSigner(), + _ => throw new Exception("Unknown System Found") + }; + _pendingTasks = new ConcurrentDictionary>(); + } + + /// + /// Send the packet and wait for the corresponding response by the packet's sequence number. + /// + public Task SendPacket(SsoPacket packet) + { + var task = new TaskCompletionSource(); + _pendingTasks.TryAdd(packet.Sequence, task); + + switch (packet.PacketType) + { + case 12: + { + var sso = SsoPacker.Build(packet, AppInfo, DeviceInfo, Keystore, SignProvider); + var service = ServicePacker.BuildProtocol12(sso, Keystore); + bool _ = Collection.Socket.Send(service.ToArray()).GetAwaiter().GetResult(); + break; + } + case 13: + { + var service = ServicePacker.BuildProtocol13(packet.Payload, Keystore, packet.Command, packet.Sequence); + bool _ = Collection.Socket.Send(service.ToArray()).GetAwaiter().GetResult(); + break; + } + } + + return task.Task; + } + + /// + /// Send the packet and don't wait for the corresponding response by the packet's sequence number. + /// + public async Task PostPacket(SsoPacket packet) + { + switch (packet.PacketType) + { + case 12: + { + var sso = SsoPacker.Build(packet, AppInfo, DeviceInfo, Keystore, SignProvider); + var service = ServicePacker.BuildProtocol12(sso, Keystore); + return await Collection.Socket.Send(service.ToArray()); + } + case 13: + { + var service = ServicePacker.BuildProtocol13(packet.Payload, Keystore, packet.Command, packet.Sequence); + return await Collection.Socket.Send(service.ToArray()); + } + default: + return false; + } + } + + public void DispatchPacket(BinaryPacket packet) + { + var service = ServicePacker.Parse(packet, Keystore); + if (service.Length == 0) return; + + var sso = SsoPacker.Parse(service); + + if (_pendingTasks.TryRemove(sso.Sequence, out var task)) + { + if (sso is { RetCode: not 0, Extra: { } extra}) + { + string msg = $"Packet '{sso.Command}' returns {sso.RetCode} with seq: {sso.Sequence}, extra: {extra}"; + task.SetException(new InvalidOperationException(msg)); + } + else + { + task.SetResult(sso); + } + } + else + { + Collection.Business.HandleServerPacket(sso); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/ServiceContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/ServiceContext.cs new file mode 100644 index 0000000..7f52021 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/ServiceContext.cs @@ -0,0 +1,131 @@ +using System.Collections.Concurrent; +using System.Reflection; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Packets; +using Lagrange.Core.Internal.Service; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context; + +/// +/// Manage the service and packet translation of the Bot +/// Instantiate the Service by and store such +/// Translate the event into , you may manually dispatch the packet to +/// +internal class ServiceContext : ContextBase +{ + private const string Tag = nameof(ServiceContext); + + private readonly SequenceProvider _sequenceProvider; + private readonly Dictionary _services; + private readonly Dictionary> _servicesEventType; + + public ServiceContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _sequenceProvider = new SequenceProvider(); + _services = new Dictionary(); + _servicesEventType = new Dictionary>(); + + RegisterServices(); + } + + private void RegisterServices() + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var type in assembly.GetDerivedTypes()) + { + _servicesEventType[type] = new List<(ServiceAttribute, IService)>(); + } + + foreach (var type in assembly.GetTypeByAttributes(out _)) + { + var serviceAttribute = type.GetCustomAttribute(); + + if (serviceAttribute != null) + { + var service = (IService)type.CreateInstance(); + _services[serviceAttribute.Command] = service; + + foreach (var attribute in type.GetCustomAttributes()) + { + _servicesEventType[attribute.EventType].Add((serviceAttribute, service)); + } + } + } + } + + /// + /// Resolve the outgoing packet by the event + /// + public List ResolvePacketByEvent(ProtocolEvent protocolEvent) + { + var result = new List(); + if (!_servicesEventType.TryGetValue(protocolEvent.GetType(), out var serviceList)) return result; // 没找到 滚蛋吧 + + foreach (var (attribute, instance) in serviceList) + { + bool success = instance.Build(protocolEvent, Keystore, AppInfo, DeviceInfo, out var binary, out var extraPackets); + + if (success && binary != null) + { + result.Add(new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), binary.ToArray())); + + if (extraPackets is { } extra) + { + var packets = extra.Select(e => new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), e.ToArray())); + result.AddRange(packets); + } + + Collection.Log.LogDebug(Tag, $"Outgoing SSOFrame: {attribute.Command}"); + } + } + + return result; + } + + /// + /// Resolve the incoming event by the packet + /// + public List ResolveEventByPacket(SsoPacket packet) + { + var result = new List(); + + if (!_services.TryGetValue(packet.Command, out var service)) + { + Collection.Log.LogWarning(Tag, $"Unsupported SSOFrame Received: {packet.Command}"); + Collection.Log.LogDebug(Tag, $"Unsuccessful SSOFrame Payload: {packet.Payload.Hex()}"); + return result; // 没找到 滚蛋吧 + } + + bool success = service.Parse(packet.Payload, Keystore, AppInfo, DeviceInfo, out var @event, out var extraEvents); + + if (success) + { + if (@event != null) result.Add(@event); + if (extraEvents != null) result.AddRange(extraEvents); + + Collection.Log.LogDebug(Tag, $"Incoming SSOFrame: {packet.Command}"); + } + + return result; + } + + public int GetNewSequence() => _sequenceProvider.GetNewSequence(); + + private class SequenceProvider + { + private readonly ConcurrentDictionary _sessionSequence = new(); + + private int _sequence = Random.Shared.Next(5000000, 9900000); + + public int GetNewSequence() + { + Interlocked.CompareExchange(ref _sequence, 5000000, 9900000); + return Interlocked.Increment(ref _sequence); + } + + public int RegisterSession(string sessionId) => _sessionSequence.GetOrAdd(sessionId, GetNewSequence()); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/SocketContext.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/SocketContext.cs new file mode 100644 index 0000000..e722f10 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/SocketContext.cs @@ -0,0 +1,140 @@ +using System.Buffers.Binary; +using System.Net; +using Lagrange.Core.Common; +using Lagrange.Core.Event.EventArg; +using Lagrange.Core.Internal.Network; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Network; + +namespace Lagrange.Core.Internal.Context; + +/// +/// Provide Low-Allocation Tcp Client which connects to the Tencent's SSO Server +/// Internal Implementation, Packet Received would be dispatched to for decryption and unpack +/// MSF Service is also implemented here +/// +internal class SocketContext : ContextBase, IClientListener +{ + private const string Tag = nameof(SocketContext); + + private readonly ClientListener _tcpClient; + + private readonly BotConfig _config; + + private Uri? ServerUri { get; set; } + + public uint HeaderSize => 4; + + public bool Connected => _tcpClient.Connected; + + public SocketContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, BotConfig config) + : base(collection, keystore, appInfo, device) + { + _tcpClient = new CallbackClientListener(this); + _config = config; + } + + public async Task Connect() + { + if (_tcpClient.Connected) return true; + + var servers = await OptimumServer(_config.GetOptimumServer, _config.UseIPv6Network); + ServerUri = servers.First(); + return await _tcpClient.Connect(ServerUri.Host, ServerUri.Port); + } + + private async Task Reconnect() + { + if (ServerUri != null && !_tcpClient.Connected) + { + bool reconnect = await _tcpClient.Connect(ServerUri.Host, ServerUri.Port); + if (reconnect) + { + Collection.Log.LogInfo(Tag, $"Reconnect to {ServerUri}"); + await Collection.Business.WtExchangeLogic.BotOnline(BotOnlineEvent.OnlineReason.Reconnect); + } + } + + return false; + } + + public void Disconnect() => _tcpClient.Disconnect(); + + public Task Send(ReadOnlyMemory packet) => _tcpClient.Send(packet); + + public uint GetPacketLength(ReadOnlySpan header) => BinaryPrimitives.ReadUInt32BigEndian(header); + + public void OnRecvPacket(ReadOnlySpan packet) + { + var binary = new BinaryPacket(packet.ToArray()); + Collection.Packet.DispatchPacket(binary); + } + + public void OnDisconnect() + { + Collection.Log.LogFatal(Tag, "Socket Disconnected, Scheduling Reconnect"); + + if (_config.AutoReconnect) + { + Collection.Scheduler.Interval("Reconnect", 10 * 1000, async () => + { + if (await Reconnect()) Collection.Scheduler.Cancel("Reconnect"); + }); + } + } + + public void OnSocketError(Exception e, ReadOnlyMemory data = default) + { + Collection.Log.LogFatal(Tag, $"Socket Error: {e.Message}"); + if (e.StackTrace != null) Collection.Log.LogFatal(Tag, e.StackTrace); + if (data.Length > 0) Collection.Log.LogDebug(Tag, $"Data: {data.Span.Hex()}"); + + _tcpClient.Disconnect(); + if (!_tcpClient.Connected) OnDisconnect(); + } + + private static readonly Uri[] HardCodeIPv6Uris = + { + new("http://msfwifiv6.3g.qq.com:14000") + }; + + /// + /// 好像这才是真货 + /// + private static readonly Uri[] TestIPv4HardCodes = + { + new("http://183.47.102.193:8080"), + new("http://14.22.9.84:8080"), + new("http://119.147.190.138:8080") + }; + + private async Task> OptimumServer(bool requestMsf, bool useIPv6Network = false) + { + + var result = requestMsf ? await ResolveDns(useIPv6Network) : useIPv6Network ? HardCodeIPv6Uris : TestIPv4HardCodes; + var latencyTasks = result.Select(uri => Icmp.PingAsync(uri)).ToArray(); + var latency = await Task.WhenAll(latencyTasks); + Array.Sort(latency, result); + + var list = result.ToList(); + for (int i = 0; i < list.Count; i++) Collection.Log.LogVerbose(Tag, $"Server: {list[i]} Latency: {latency[i]}"); + return list; + } + + private static async Task ResolveDns(bool useIPv6Network = false) + { + string dns = useIPv6Network ? "msfwifiv6.3g.qq.com" : "msfwifi.3g.qq.com"; + var addresses = await Dns.GetHostEntryAsync(dns); + var result = new Uri[addresses.AddressList.Length]; + + for (int i = 0; i < addresses.AddressList.Length; i++) result[i] = new Uri($"http://{addresses.AddressList[i]}:8080"); + + return result; + } + + public void Dispose() + { + _tcpClient.Disconnect(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/Common.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/Common.cs new file mode 100644 index 0000000..8548a2f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/Common.cs @@ -0,0 +1,113 @@ +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Highway; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Crypto.Provider.Sha; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Uploader; + +internal static class Common +{ + private const int BlockSize = 1024 * 1024; + + public static NTV2RichMediaHighwayExt? GenerateExt(NTV2RichMediaUploadEvent @event) + { + if (@event.UKey == null) return null; + + var index = @event.MsgInfo.MsgInfoBody[0].Index; + return new NTV2RichMediaHighwayExt + { + FileUuid = index.FileUuid, + UKey = @event.UKey, + Network = Convert(@event.Network), + MsgInfoBody = @event.MsgInfo.MsgInfoBody, + BlockSize = BlockSize, + Hash = new NTHighwayHash + { + FileSha1 = new List { index.Info.FileSha1.UnHex() } + } + }; + } + + public static NTV2RichMediaHighwayExt? GenerateExt(NTV2RichMediaUploadEvent @event, SubFileInfo subFile) + { + if (subFile.UKey == null) return null; + + var index = @event.MsgInfo.MsgInfoBody[1].Index; + return new NTV2RichMediaHighwayExt + { + FileUuid = index.FileUuid, + UKey = subFile.UKey, + Network = Convert(subFile.IPv4s), + MsgInfoBody = @event.MsgInfo.MsgInfoBody, + BlockSize = BlockSize, + Hash = new NTHighwayHash + { + FileSha1 = new List { index.Info.FileSha1.UnHex() } + } + }; + } + + public static async Task GetTicket(ContextCollection context) + { + var hwUrlEvent = HighwayUrlEvent.Create(); + var highwayUrlResult = await context.Business.SendEvent(hwUrlEvent); + return ((HighwayUrlEvent)highwayUrlResult[0]).SigSession; + } + + public static List CalculateStreamBytes(Stream inputStream) + { + const int blockSize = 1024 * 1024; + + inputStream.Seek(0, SeekOrigin.Begin); + var byteArrayList = new List(); + var sha1 = new Sha1Stream(); + + var buffer = new byte[Sha1Stream.Sha1BlockSize]; + var digest = new byte[Sha1Stream.Sha1DigestSize]; + int lastRead; + + while (true) + { + int read = inputStream.Read(buffer); + if (read < Sha1Stream.Sha1BlockSize) + { + lastRead = read; + break; + } + + sha1.Update(buffer, Sha1Stream.Sha1BlockSize); + if (inputStream.Position % blockSize == 0) + { + sha1.Hash(digest, false); + byteArrayList.Add((byte[])digest.Clone()); + } + } + + sha1.Update(buffer, lastRead); + sha1.Final(digest); + byteArrayList.Add((byte[])digest.Clone()); + + return byteArrayList; + } + + private static NTHighwayNetwork Convert(List ipv4s) => new() + { + IPv4s = ipv4s.Select(x => new NTHighwayIPv4 + { + Domain = new NTHighwayDomain + { + IsEnable = true, + IP = ConvertIP(x.OutIP) + }, + Port = x.OutPort + }).ToList() + }; + + private static string ConvertIP(uint raw) + { + var ip = BitConverter.GetBytes(raw); + return $"{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/FileUploader.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/FileUploader.cs new file mode 100644 index 0000000..abad6c6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/FileUploader.cs @@ -0,0 +1,158 @@ +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Highway; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Uploader; + +/// +/// This FileUploader should be called manually +/// +internal static class FileUploader +{ + public static async Task UploadPrivate(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is not FileEntity { FileStream: not null } file) return false; + + var uploadEvent = FileUploadEvent.Create(chain.Uid ?? "", file); + var result = await context.Business.SendEvent(uploadEvent); + var uploadResp = (FileUploadEvent)result[0]; + + if (!uploadResp.IsExist) + { + var ext = new FileUploadExt + { + Unknown1 = 100, + Unknown2 = 1, + Entry = new FileUploadEntry + { + BusiBuff = new ExcitingBusiInfo + { + SenderUin = context.Keystore.Uin, + }, + FileEntry = new ExcitingFileEntry + { + FileSize = file.FileStream.Length, + Md5 = file.FileMd5, + CheckKey = file.FileSha1, + Md5S2 = file.FileMd5, + FileId = uploadResp.FileId, + UploadKey = uploadResp.UploadKey + }, + ClientInfo = new ExcitingClientInfo + { + ClientType = 3, + AppId = "100", + TerminalType = 3, + ClientVer = "1.1.1", + Unknown = 4 + }, + FileNameInfo = new ExcitingFileNameInfo + { + FileName = file.FileName + }, + Host = new ExcitingHostConfig + { + Hosts = new List + { + new() + { + Url = new ExcitingUrlInfo + { + Unknown = 1, + Host = uploadResp.Ip + }, + Port = uploadResp.Port + } + } + } + }, + Unknown200 = 1 + }; + + file.FileHash = uploadResp.Addon; + file.FileUuid = uploadResp.FileId; + + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(95, file.FileStream, await Common.GetTicket(context), file.FileMd5, ext.Serialize().ToArray()); + if (!hwSuccess) return false; + } + + await file.FileStream.DisposeAsync(); + var sendEvent = SendMessageEvent.Create(chain); + var sendResult = await context.Business.SendEvent(sendEvent); + return sendResult.Count != 0 && ((SendMessageEvent)sendResult[0]).MsgResult.Result == 0; + } + + public static async Task UploadGroup(ContextCollection context, MessageChain chain, IMessageEntity entity, string targetDirectory) + { + if (entity is not FileEntity { FileStream: not null } file) return false; + + var uploadEvent = GroupFSUploadEvent.Create(chain.GroupUin ?? 0, targetDirectory, file); + var result = await context.Business.SendEvent(uploadEvent); + var uploadResp = (GroupFSUploadEvent)result[0]; + + + if (!uploadResp.IsExist) + { + var ext = new FileUploadExt + { + Unknown1 = 100, + Unknown2 = 1, + Entry = new FileUploadEntry + { + BusiBuff = new ExcitingBusiInfo + { + SenderUin = context.Keystore.Uin, + ReceiverUin = chain.GroupUin ?? 0, + GroupCode = chain.GroupUin ?? 0 + }, + FileEntry = new ExcitingFileEntry + { + FileSize = file.FileStream.Length, + Md5 = file.FileMd5, + CheckKey = uploadResp.CheckKey, + Md5S2 = file.FileMd5, + FileId = uploadResp.FileId, + UploadKey = uploadResp.UploadKey + }, + ClientInfo = new ExcitingClientInfo + { + ClientType = 3, + AppId = "100", + TerminalType = 3, + ClientVer = "1.1.1", + Unknown = 4 + }, + FileNameInfo = new ExcitingFileNameInfo + { + FileName = file.FileName + }, + Host = new ExcitingHostConfig + { + Hosts = new List + { + new() + { + Url = new ExcitingUrlInfo + { + Unknown = 1, + Host = uploadResp.Ip + }, + Port = uploadResp.Port + } + } + } + } + }; + + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(71, file.FileStream, await Common.GetTicket(context), file.FileMd5, ext.Serialize().ToArray()); + if (!hwSuccess) return false; + } + + await file.FileStream.DisposeAsync(); + var sendEvent = GroupSendFileEvent.Create(chain.GroupUin ?? 0, uploadResp.FileId); + var sendResult = await context.Business.SendEvent(sendEvent); + return sendResult.Count != 0 && ((GroupSendFileEvent)sendResult[0]).ResultCode == 0; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/HighwayUploaderAttribute.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/HighwayUploaderAttribute.cs new file mode 100644 index 0000000..fb974ab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/HighwayUploaderAttribute.cs @@ -0,0 +1,9 @@ +namespace Lagrange.Core.Internal.Context.Uploader; + +[AttributeUsage(AttributeTargets.Class)] +internal class HighwayUploaderAttribute : Attribute +{ + public Type Entity { get; } + + public HighwayUploaderAttribute(Type entity) => Entity = entity; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/IHighwayUploader.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/IHighwayUploader.cs new file mode 100644 index 0000000..7af032b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/IHighwayUploader.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Context.Uploader; + +internal interface IHighwayUploader +{ + public Task UploadPrivate(ContextCollection context, MessageChain chain, IMessageEntity entity); + + public Task UploadGroup(ContextCollection context, MessageChain chain, IMessageEntity entity); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/ImageUploader.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/ImageUploader.cs new file mode 100644 index 0000000..14b0d72 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/ImageUploader.cs @@ -0,0 +1,60 @@ +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Uploader; + +[HighwayUploader(typeof(ImageEntity))] +internal class ImageUploader : IHighwayUploader +{ + public async Task UploadPrivate(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is ImageEntity { ImageStream: { } stream } image) + { + var uploadEvent = ImageUploadEvent.Create(image, chain.FriendInfo?.Uid ?? ""); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (ImageUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1003, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + image.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + image.CompatImage = metaResult.Compat; // for legacy QQ + await image.ImageStream.Value.DisposeAsync(); + } + } + + public async Task UploadGroup(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is ImageEntity { ImageStream: { } stream } image) + { + var uploadEvent = ImageGroupUploadEvent.Create(image, chain.GroupUin ?? 0); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (ImageGroupUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1004, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + image.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + image.CompatFace = metaResult.Compat; // for legacy QQ + await image.ImageStream.Value.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/PttUploader.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/PttUploader.cs new file mode 100644 index 0000000..f2bd489 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/PttUploader.cs @@ -0,0 +1,60 @@ +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Uploader; + +[HighwayUploader(typeof(RecordEntity))] +internal class PttUploader : IHighwayUploader +{ + public async Task UploadPrivate(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is RecordEntity { AudioStream: { } stream } record) + { + var uploadEvent = RecordUploadEvent.Create(record, chain.FriendInfo?.Uid ?? ""); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (RecordUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1007, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + record.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + record.Compat = metaResult.Compat; // for legacy QQ + await record.AudioStream.Value.DisposeAsync(); + } + } + + public async Task UploadGroup(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is RecordEntity { AudioStream: { } stream } record) + { + var uploadEvent = RecordGroupUploadEvent.Create(record, chain.GroupUin ?? 0); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (RecordGroupUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1008, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + record.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + record.Compat = metaResult.Compat; // for legacy QQ + await record.AudioStream.Value.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/VideoUploader.cs b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/VideoUploader.cs new file mode 100644 index 0000000..5e5df60 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Context/Uploader/VideoUploader.cs @@ -0,0 +1,90 @@ +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Context.Uploader; + +[HighwayUploader(typeof(VideoEntity))] +internal class VideoUploader : IHighwayUploader +{ + public async Task UploadPrivate(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is VideoEntity { VideoStream: { } stream, ThumbnailStream: { } thumbnail } video) + { + var uploadEvent = VideoUploadEvent.Create(video, chain.FriendInfo?.Uid ?? ""); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (VideoUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + ext.Hash.FileSha1 = Common.CalculateStreamBytes(stream.Value); + + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1001, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + if (Common.GenerateExt(metaResult, metaResult.SubFiles[0]) is { } subExt) + { + var hash = metaResult.MsgInfo.MsgInfoBody[1].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1002, thumbnail.Value, await Common.GetTicket(context), hash, subExt.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + await thumbnail.Value.DisposeAsync(); + throw new Exception(); + } + } + + video.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + video.Compat = metaResult.Compat; // for legacy QQ + await stream.Value.DisposeAsync(); + await thumbnail.Value.DisposeAsync(); + } + } + + public async Task UploadGroup(ContextCollection context, MessageChain chain, IMessageEntity entity) + { + if (entity is VideoEntity { VideoStream: { } stream, ThumbnailStream: { } thumbnail } video) + { + var uploadEvent = VideoGroupUploadEvent.Create(video, chain.GroupUin ?? 0); + var uploadResult = await context.Business.SendEvent(uploadEvent); + var metaResult = (VideoGroupUploadEvent)uploadResult[0]; + + if (Common.GenerateExt(metaResult) is { } ext) + { + ext.Hash.FileSha1 = Common.CalculateStreamBytes(stream.Value); + + var hash = metaResult.MsgInfo.MsgInfoBody[0].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1005, stream.Value, await Common.GetTicket(context), hash, ext.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + throw new Exception(); + } + } + + if (Common.GenerateExt(metaResult, metaResult.SubFiles[0]) is { } subExt) + { + var hash = metaResult.MsgInfo.MsgInfoBody[1].Index.Info.FileHash.UnHex(); + bool hwSuccess = await context.Highway.UploadSrcByStreamAsync(1006, thumbnail.Value, await Common.GetTicket(context), hash, subExt.Serialize().ToArray()); + if (!hwSuccess) + { + await stream.Value.DisposeAsync(); + await thumbnail.Value.DisposeAsync(); + throw new Exception(); + } + } + + video.MsgInfo = metaResult.MsgInfo; // directly constructed by Tencent's BDH Server + video.Compat = metaResult.Compat; // for legacy QQ + await stream.Value.DisposeAsync(); + await thumbnail.Value.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Core.md b/Lagrange.Core/Lagrange.Core/Internal/Core.md new file mode 100644 index 0000000..a83a928 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Core.md @@ -0,0 +1,7 @@ +# Lagrange.Core +## Core + +This is the core of the Lagrange framework. It contains the basic classes and interfaces that are used throughout the framework. + + +Most of the internal data structures are implemented in this namespace \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/DeleteFriendEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/DeleteFriendEvent.cs new file mode 100644 index 0000000..05e1d5b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/DeleteFriendEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class DeleteFriendEvent : ProtocolEvent +{ + public string? TargetUid { get; set; } + + public bool Block { get; set; } + + private DeleteFriendEvent(string? targetUid, bool block) : base(true) + { + TargetUid = targetUid; + Block = block; + } + + private DeleteFriendEvent(int resultCode) : base(resultCode) { } + + public static DeleteFriendEvent Create(string? targetUid, bool block) => new (targetUid, block); + + public static DeleteFriendEvent Result(int resultCode) => new (resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs new file mode 100644 index 0000000..3c1bdd7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchAiCharacterListEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint ChatType { get; set; } + + public List? AiCharacters { get; set; } + + public string ErrorMessage { get; set; }=string.Empty; + + private FetchAiCharacterListEvent(uint chatType, uint groupUin = 42) : base(true) + { + ChatType = chatType; + GroupUin = groupUin; + AiCharacters = new List(); + } + + private FetchAiCharacterListEvent(int resultCode, List? aiCharacters,string errMsg) : base(resultCode) + { + AiCharacters = aiCharacters; + ErrorMessage = errMsg; + } + + public static FetchAiCharacterListEvent Create(uint chatType, uint groupUin) => new(chatType, groupUin); + + public static FetchAiCharacterListEvent Result(int resultCode, List? aiCharacters,string errMsg) => + new(resultCode, aiCharacters, errMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchCustomFaceEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchCustomFaceEvent.cs new file mode 100644 index 0000000..94cc503 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchCustomFaceEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchCustomFaceEvent : ProtocolEvent +{ + public List Urls { get; } = new(); + + private FetchCustomFaceEvent() : base(true) { } + + private FetchCustomFaceEvent(int resultCode, List urls) : base(resultCode) + { + Urls = urls; + } + + public static FetchCustomFaceEvent Create() => new(); + + public static FetchCustomFaceEvent Result(int resultCode, List urls) => new(resultCode, urls); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchGroupAtAllRemainEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchGroupAtAllRemainEvent.cs new file mode 100644 index 0000000..c9580c4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchGroupAtAllRemainEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchGroupAtAllRemainEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint RemainAtAllCountForUin { get; set; } + + public uint RemainAtAllCountForGroup { get; set; } + + private FetchGroupAtAllRemainEvent(uint groupUin) : base(true) + { + GroupUin = groupUin; + } + + private FetchGroupAtAllRemainEvent(int resultCode, uint remainAtAllCountForUin, uint remainAtAllCountForGroup) : base(resultCode) + { + RemainAtAllCountForUin = remainAtAllCountForUin; + RemainAtAllCountForGroup = remainAtAllCountForGroup; + } + + public static FetchGroupAtAllRemainEvent Create(uint groupUin) => new(groupUin); + + public static FetchGroupAtAllRemainEvent Result(int resultCode, uint remainAtAllCountForUin, uint remainAtAllCountForGroup) => + new(resultCode, remainAtAllCountForUin, remainAtAllCountForGroup); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchMarketFaceKeyEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchMarketFaceKeyEvent.cs new file mode 100644 index 0000000..38b5bb9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchMarketFaceKeyEvent.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchMarketFaceKeyEvent : ProtocolEvent +{ + public List FaceIds { get; } = new(); + + public List? Keys { get; } = new(); + + private FetchMarketFaceKeyEvent(List faceIds) : base(true) + { + FaceIds = faceIds; + } + + protected FetchMarketFaceKeyEvent(int resultCode, List? keys) : base(resultCode) + { + Keys = keys; + } + + public static FetchMarketFaceKeyEvent Create(List faceIds) => new(faceIds); + + public static FetchMarketFaceKeyEvent Result(int resultCode, List? keys) => new(resultCode, keys); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchPinsEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchPinsEvent.cs new file mode 100644 index 0000000..de76723 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FetchPinsEvent.cs @@ -0,0 +1,42 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchPinsEvent : ProtocolEvent +{ + internal List FriendUids { get; set; } + + public List FriendUins { get; set; } + + public List GroupUins { get; set; } + + public string Message { get; set; } + + protected FetchPinsEvent() : base(true) + { + FriendUids = new(); + FriendUins = new(); + GroupUins = new(); + Message = string.Empty; + } + + protected FetchPinsEvent(List friendUids, List groupUins) : base(0) + { + FriendUids = friendUids; + FriendUins = new(); + GroupUins = groupUins; + Message = string.Empty; + } + + protected FetchPinsEvent(int retcode, string message) : base(retcode) + { + FriendUids = new(); + FriendUins = new(); + GroupUins = new(); + Message = string.Empty; + } + + public static FetchPinsEvent Create() => new(); + + public static FetchPinsEvent Result(List friendUids, List groupUins) => new(friendUids, groupUins); + + public static FetchPinsEvent Result(int retcode, string message) => new(retcode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendJoinEmojiChainEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendJoinEmojiChainEvent.cs new file mode 100644 index 0000000..1846f50 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendJoinEmojiChainEvent.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FriendJoinEmojiChainEvent : JoinEmojiChainEvent +{ + private FriendJoinEmojiChainEvent(uint targetMessageSeq, uint targetFaceId, string friendUid) : base(targetMessageSeq, targetFaceId) + { + FriendUid = friendUid; + } + + private FriendJoinEmojiChainEvent(int resultCode) : base(resultCode) { } + + public static FriendJoinEmojiChainEvent Create(uint targetMessageSeq, uint targetFaceId, string friendUid) + => new(targetMessageSeq, targetFaceId, friendUid); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendLikeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendLikeEvent.cs new file mode 100644 index 0000000..fc362d3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/FriendLikeEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class FriendLikeEvent : ProtocolEvent +{ + public string TargetUid { get; } = ""; + + public uint Count { get; } + + private FriendLikeEvent(string targetUid, uint count) : base(true) + { + TargetUid = targetUid; + Count = count; + } + + private FriendLikeEvent(int resultCode) : base(resultCode) { } + + public static FriendLikeEvent Create(string targetUid, uint count) => new(targetUid, count); + + public static FriendLikeEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAddReactionEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAddReactionEvent.cs new file mode 100644 index 0000000..c6b848d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAddReactionEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupAddReactionEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public string Code { get; } = string.Empty; + + public bool IsEmoji => Code.Length > 3; + + private GroupAddReactionEvent(uint groupUin, uint sequence, string code) : base(true) + { + GroupUin = groupUin; + Sequence = sequence; + Code = code; + } + + private GroupAddReactionEvent(int resultCode) : base(resultCode) { } + + public static GroupAddReactionEvent Create(uint groupUin, uint sequence, string code) + => new(groupUin, sequence, code); + + public static GroupAddReactionEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs new file mode 100644 index 0000000..b49843f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupAiRecordEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + public string Character { get; set; } = string.Empty; + public string Text { get; set; } = string.Empty; + public uint ChatType { get; set; } + public uint ChatId { get; set; } + + public MsgInfo? RecordInfo { get; set; } + public string ErrorMessage { get; set; } = string.Empty; + + private GroupAiRecordEvent(uint groupUin, string character, string text, uint chatType, uint chatId) : base(0) + { + GroupUin = groupUin; + Character = character; + Text = text; + ChatType = chatType; + ChatId = chatId; + } + + private GroupAiRecordEvent(int resultCode, MsgInfo? msgInfo) : base(resultCode) + { + RecordInfo = msgInfo; + } + + private GroupAiRecordEvent(int resultCode, string errMsg) : base(resultCode) + { + ErrorMessage = errMsg; + } + + public static GroupAiRecordEvent Create(uint groupUin, string character, string text, uint chatType, uint chatId) => + new(groupUin, character, text, chatType, chatId); + + public static GroupAiRecordEvent Result(int resultCode, MsgInfo? msgInfo) => new(resultCode, msgInfo); + + public static GroupAiRecordEvent Result(int resultCode, string errMessage) => new(resultCode, errMessage); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupClockInEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupClockInEvent.cs new file mode 100644 index 0000000..e7d1c62 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupClockInEvent.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupClockInEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + public BotGroupClockInResult? ResultInfo { get; set; } + + private GroupClockInEvent(uint groupUin) : base(true) + { + GroupUin = groupUin; + ResultInfo = null; + } + + private GroupClockInEvent(int resultCode, BotGroupClockInResult result) : base(resultCode) + { + ResultInfo = result; + } + + public static GroupClockInEvent Create(uint groupUin) => new(groupUin); + + public static GroupClockInEvent Result(int resultCode, BotGroupClockInResult result) => new(resultCode, result); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCountEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCountEvent.cs new file mode 100644 index 0000000..1844981 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCountEvent.cs @@ -0,0 +1,24 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSCountEvent : GroupFSViewEvent +{ + public uint FileCount { get; set; } + + public uint LimitCount { get; set; } + + public bool IsFull { get; set; } + + private GroupFSCountEvent(uint groupUin) : base(groupUin) { } + + private GroupFSCountEvent(int resultCode, uint fileCount, uint limitCount, bool isFull) : base(resultCode) + { + FileCount = fileCount; + LimitCount = limitCount; + IsFull = isFull; + } + + public static GroupFSCountEvent Create(uint groupUin) => new(groupUin); + + public static GroupFSCountEvent Result(int resultCode, uint fileCount, uint limitCount, bool isFull) + => new(resultCode, fileCount, limitCount, isFull); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCreateFolderEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCreateFolderEvent.cs new file mode 100644 index 0000000..bda99c7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSCreateFolderEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSCreateFolderEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string Name { get; } = string.Empty; + + public string RetMsg { get; set; } = string.Empty; + + private GroupFSCreateFolderEvent(uint groupUin, string name) : base(true) + { + GroupUin = groupUin; + Name = name; + } + + private GroupFSCreateFolderEvent(int resultCode, string retMsg) : base(resultCode) + { + RetMsg = retMsg; + } + + public static GroupFSCreateFolderEvent Create(uint groupUin, string name) => new(groupUin, name); + + public static GroupFSCreateFolderEvent Result(int resultCode, string retMsg) => new(resultCode, retMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteEvent.cs new file mode 100644 index 0000000..4039b21 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupFSDeleteEvent : GroupFSOperationEvent +{ + public string FileId { get; set; } + + public string RetMsg { get; set; } = string.Empty; + + public GroupFSDeleteEvent(uint groupUin, string fileId) : base(groupUin) + { + FileId = fileId; + } + + public GroupFSDeleteEvent(int resultCode, string retMsg) : base(resultCode) + { + RetMsg = retMsg; + } + + public static GroupFSDeleteEvent Create(uint groupUin, string fileId) + => new(groupUin, fileId); + + public static GroupFSDeleteEvent Result(int resultCode, string retMsg) => new(resultCode, retMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteFolderEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteFolderEvent.cs new file mode 100644 index 0000000..b9c2a13 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDeleteFolderEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSDeleteFolderEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string FolderId { get; } = string.Empty; + + public string RetMsg { get; set; } = string.Empty; + + private GroupFSDeleteFolderEvent(uint groupUin, string folderId) : base(true) + { + GroupUin = groupUin; + FolderId = folderId; + } + + private GroupFSDeleteFolderEvent(int resultCode, string retMsg) : base(resultCode) + { + RetMsg = retMsg; + } + + public static GroupFSDeleteFolderEvent Create(uint groupUin, string folderId) => new(groupUin, folderId); + + public static GroupFSDeleteFolderEvent Result(int resultCode, string retMsg) => new(resultCode, retMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDownloadEvent.cs new file mode 100644 index 0000000..ad663c4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSDownloadEvent.cs @@ -0,0 +1,24 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSDownloadEvent : GroupFSOperationEvent +{ + public string FileUrl { get; set; } + + public string FileId { get; set; } + + private GroupFSDownloadEvent(uint groupUin, string fileId) : base(groupUin) + { + FileId = fileId; + FileUrl = ""; + } + + private GroupFSDownloadEvent(int resultCode, string fileUrl) : base(resultCode) + { + FileUrl = fileUrl; + FileId = ""; + } + + public static GroupFSDownloadEvent Create(uint groupUin, string fileId) => new(groupUin, fileId); + + public static GroupFSDownloadEvent Result(int resultCode, string fileId) => new(resultCode, fileId); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSListEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSListEvent.cs new file mode 100644 index 0000000..be2e05b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSListEvent.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupFSListEvent : GroupFSViewEvent +{ + public string TargetDirectory { get; set; } + + public uint StartIndex { get; set; } + + public uint FileCount { get; set; } + + public bool IsEnd { get; set; } + + public List FileEntries { get; set; } + + private GroupFSListEvent(uint groupUin, string targetDirectory, uint startIndex, uint fileCount) : base(groupUin) + { + TargetDirectory = targetDirectory; + StartIndex = startIndex; + FileCount = fileCount; + IsEnd = false; + } + + private GroupFSListEvent(int resultCode, List fileEntries, bool isEnd) : base(resultCode) + { + FileEntries = fileEntries; + IsEnd = isEnd; + } + + public static GroupFSListEvent Create(uint groupUin, string targetDirectory, uint startIndex, uint fileCount) + => new(groupUin, targetDirectory, startIndex, fileCount); + + public static GroupFSListEvent Result(int resultCode, List fileEntries, bool isEnd) + => new(resultCode, fileEntries, isEnd); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSMoveEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSMoveEvent.cs new file mode 100644 index 0000000..265c073 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSMoveEvent.cs @@ -0,0 +1,31 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupFSMoveEvent : GroupFSOperationEvent +{ + public string FileId { get; set; } + + public string ParentDirectory { get; set; } + + public string TargetDirectory { get; set; } + + public string RetMsg { get; set; } = string.Empty; + + private GroupFSMoveEvent(uint groupUin, string fileId, string parentDirectory, string targetDirectory) : base(groupUin) + { + FileId = fileId; + ParentDirectory = parentDirectory; + TargetDirectory = targetDirectory; + } + + private GroupFSMoveEvent(int resultCode, string retMsg) : base(resultCode) + { + RetMsg = retMsg; + } + + public static GroupFSMoveEvent Create(uint groupUin, string fileId, string parentDirectory, string targetDirectory) + => new(groupUin, fileId, parentDirectory, targetDirectory); + + public static GroupFSMoveEvent Result(int resultCode, string retMsg) => new(resultCode, retMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSOperationEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSOperationEvent.cs new file mode 100644 index 0000000..9672652 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSOperationEvent.cs @@ -0,0 +1,13 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal abstract class GroupFSOperationEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + protected GroupFSOperationEvent(uint groupUin) : base(true) + { + GroupUin = groupUin; + } + + protected GroupFSOperationEvent(int resultCode) : base(resultCode) { } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameEvent.cs new file mode 100644 index 0000000..c8a682f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameEvent.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupFSRenameEvent : GroupFSOperationEvent +{ + public string FileId { get; set; } + + public string ParentDirectory { get; set; } + + public string TargetDirectory { get; set; } + + private GroupFSRenameEvent(uint groupUin, string fileId, string parentDirectory, string targetDirectory) : base(groupUin) + { + FileId = fileId; + ParentDirectory = parentDirectory; + TargetDirectory = targetDirectory; + } + + private GroupFSRenameEvent(int resultCode) : base(resultCode) { } + + public static GroupFSRenameEvent Create(uint groupUin, string fileId, string parentDirectory, string targetDirectory) + => new(groupUin, fileId, parentDirectory, targetDirectory); + + public static GroupFSRenameEvent Result(int resultCode) + => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameFolderEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameFolderEvent.cs new file mode 100644 index 0000000..259c4c8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSRenameFolderEvent.cs @@ -0,0 +1,28 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSRenameFolderEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string FolderId { get; } = string.Empty; + + public string NewFolderName { get; } = string.Empty; + + public string RetMsg { get; set; } = string.Empty; + + private GroupFSRenameFolderEvent(uint groupUin, string folderId, string newFolderName) : base(true) + { + GroupUin = groupUin; + FolderId = folderId; + NewFolderName = newFolderName; + } + + private GroupFSRenameFolderEvent(int resultCode, string retMsg) : base(resultCode) + { + RetMsg = retMsg; + } + + public static GroupFSRenameFolderEvent Create(uint groupUin, string folderId, string newFolderName) => new(groupUin, folderId, newFolderName); + + public static GroupFSRenameFolderEvent Result(int resultCode, string retMsg) => new(resultCode, retMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSSpaceEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSSpaceEvent.cs new file mode 100644 index 0000000..684f4a6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSSpaceEvent.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFSSpaceEvent : GroupFSViewEvent +{ + public ulong TotalSpace { get; set; } + + public ulong UsedSpace { get; set; } + + private GroupFSSpaceEvent(uint groupUin) : base(groupUin) { } + + private GroupFSSpaceEvent(int resultCode, ulong totalSpace, ulong usedSpace) : base(resultCode) + { + TotalSpace = totalSpace; + UsedSpace = usedSpace; + } + + public static GroupFSSpaceEvent Create(uint groupUin) => new(groupUin); + + public static GroupFSSpaceEvent Result(int resultCode, ulong totalSpace, ulong usedSpace) => + new(resultCode, totalSpace, usedSpace); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSViewEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSViewEvent.cs new file mode 100644 index 0000000..e456776 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFSViewEvent.cs @@ -0,0 +1,13 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal abstract class GroupFSViewEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + protected GroupFSViewEvent(uint groupUin) : base(true) + { + GroupUin = groupUin; + } + + protected GroupFSViewEvent(int resultCode) : base(resultCode) { } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFinishTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFinishTodoEvent.cs new file mode 100644 index 0000000..6ab54cf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupFinishTodoEvent.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupFinishTodoEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string? ResultMessage { get; set; } + + private GroupFinishTodoEvent(uint groupUin) : base(0) + { + GroupUin = groupUin; + } + + private GroupFinishTodoEvent(int resultCode, string? message) : base(resultCode) { + ResultMessage = message; + } + + public static GroupFinishTodoEvent Create(uint groupUin) => new(groupUin); + + public static GroupFinishTodoEvent Result(int resultCode, string? message) => new(resultCode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupGetTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupGetTodoEvent.cs new file mode 100644 index 0000000..2d34c96 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupGetTodoEvent.cs @@ -0,0 +1,38 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupGetTodoEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint Sequence { get; set; } + + public string Preview { get; set; } + + public string? ResultMessage { get; set; } + + private GroupGetTodoEvent(uint groupUin) : base(0) { + GroupUin = groupUin; + Preview = string.Empty; + } + + private GroupGetTodoEvent(int resultCode, string? message, uint groupUin, uint sequence, string preview) : base(resultCode) + { + ResultMessage = message; + + GroupUin = groupUin; + Sequence = sequence; + Preview = preview; + } + + private GroupGetTodoEvent(int resultCode, string? message) : base(resultCode) + { + ResultMessage = message; + Preview = string.Empty; + } + + public static GroupGetTodoEvent Create(uint groupUin) => new(groupUin); + + public static GroupGetTodoEvent Result(int resultCode, string? message) => Result(resultCode, message, 0, 0, string.Empty); + + public static GroupGetTodoEvent Result(int resultCode, string? message, uint groupUin, uint sequence, string preview) => new(resultCode, message, groupUin, sequence, preview); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupInviteEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupInviteEvent.cs new file mode 100644 index 0000000..0d2a22a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupInviteEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupInviteEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public Dictionary InviteUids { get; } + + private GroupInviteEvent(uint groupUin, Dictionary inviteUids) : base(true) + { + GroupUin = groupUin; + InviteUids = inviteUids; + } + + private GroupInviteEvent(int resultCode) : base(resultCode) { } + + public static GroupInviteEvent Create(uint groupUin, Dictionary inviteUids) + => new(groupUin, inviteUids); + + public static GroupInviteEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupJoinEmojiChainEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupJoinEmojiChainEvent.cs new file mode 100644 index 0000000..8f58544 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupJoinEmojiChainEvent.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupJoinEmojiChainEvent : JoinEmojiChainEvent +{ + private GroupJoinEmojiChainEvent(uint targetMessageSeq, uint targetFaceId, uint groupUin) : base(targetMessageSeq, targetFaceId) + { + GroupUin = groupUin; + } + + private GroupJoinEmojiChainEvent(int resultCode) : base(resultCode) { } + + public static GroupJoinEmojiChainEvent Create(uint targetMessageSeq, uint targetFaceId, uint groupUin) + => new(targetMessageSeq, targetFaceId, groupUin); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupKickMemberEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupKickMemberEvent.cs new file mode 100644 index 0000000..5b84826 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupKickMemberEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupKickMemberEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string Uid { get; set; } = ""; + + public bool RejectAddRequest { get; set; } + + public string Reason { get; set; } = string.Empty; + + private GroupKickMemberEvent(uint groupUin, string uid, bool rejectAddRequest, string reason) : base(0) + { + GroupUin = groupUin; + Uid = uid; + RejectAddRequest = rejectAddRequest; + Reason = reason; + } + + private GroupKickMemberEvent(int resultCode) : base(resultCode) { } + + public static GroupKickMemberEvent Create(uint groupUin, string uid, bool rejectAddRequest, string reason) => new(groupUin, uid, rejectAddRequest, reason); + + public static GroupKickMemberEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupLeaveEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupLeaveEvent.cs new file mode 100644 index 0000000..2788372 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupLeaveEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupLeaveEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + private GroupLeaveEvent(uint groupUin) : base(true) + { + GroupUin = groupUin; + } + + private GroupLeaveEvent(int resultCode) : base(resultCode) { } + + public static GroupLeaveEvent Create(uint groupUin) => new(groupUin); + + public static GroupLeaveEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteGlobalEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteGlobalEvent.cs new file mode 100644 index 0000000..38d7383 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteGlobalEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupMuteGlobalEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public bool IsMute { get; set; } + + private GroupMuteGlobalEvent(uint groupUin, bool isMute) : base(true) + { + GroupUin = groupUin; + IsMute = isMute; + } + + private GroupMuteGlobalEvent(int resultCode) : base(resultCode) { } + + public static GroupMuteGlobalEvent Create(uint groupUin, bool isMute) => new(groupUin, isMute); + + public static GroupMuteGlobalEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteMemberEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteMemberEvent.cs new file mode 100644 index 0000000..77796e3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupMuteMemberEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupMuteMemberEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint Duration { get; set; } + + public string Uid { get; set; } = ""; + + private GroupMuteMemberEvent(uint groupUin, uint duration, string uid) : base(0) + { + GroupUin = groupUin; + Duration = duration; + Uid = uid; + } + + private GroupMuteMemberEvent(int resultCode) : base(resultCode) { } + + public static GroupMuteMemberEvent Create(uint groupUin, uint duration, string uid) => new(groupUin, duration, uid); + + public static GroupMuteMemberEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupReduceReactionEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupReduceReactionEvent.cs new file mode 100644 index 0000000..005ff67 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupReduceReactionEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupReduceReactionEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public string Code { get; } = string.Empty; + + public bool IsEmoji => Code.Length > 3; + + private GroupReduceReactionEvent(uint groupUin, uint sequence, string code) : base(true) + { + GroupUin = groupUin; + Sequence = sequence; + Code = code; + } + + private GroupReduceReactionEvent(int resultCode) : base(resultCode) { } + + public static GroupReduceReactionEvent Create(uint groupUin, uint sequence, string code) + => new(groupUin, sequence, code); + + public static GroupReduceReactionEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemarkEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemarkEvent.cs new file mode 100644 index 0000000..69e2669 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemarkEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupRemarkEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string TargetRemark { get; set; } = ""; + + private GroupRemarkEvent(uint groupUin, string targetRemark) : base(true) + { + GroupUin = groupUin; + TargetRemark = targetRemark; + } + + private GroupRemarkEvent(int resultCode) : base(resultCode) { } + + public static GroupRemarkEvent Create(uint groupUin, string targetRemark) => new(groupUin, targetRemark); + + public static GroupRemarkEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemoveTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemoveTodoEvent.cs new file mode 100644 index 0000000..0534d2f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRemoveTodoEvent.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupRemoveTodoEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string? ResultMessage { get; set; } + + private GroupRemoveTodoEvent(uint groupUin) : base(0) + { + GroupUin = groupUin; + } + + private GroupRemoveTodoEvent(int resultCode, string? message) : base(resultCode) { + ResultMessage = message; + } + + public static GroupRemoveTodoEvent Create(uint groupUin) => new(groupUin); + + public static GroupRemoveTodoEvent Result(int resultCode, string? message) => new(resultCode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRenameEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRenameEvent.cs new file mode 100644 index 0000000..db575cb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupRenameEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupRenameEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string TargetName { get; set; } = ""; + + private GroupRenameEvent(uint groupUin, string targetName) : base(true) + { + GroupUin = groupUin; + TargetName = targetName; + } + + private GroupRenameEvent(int resultCode) : base(resultCode) { } + + public static GroupRenameEvent Create(uint groupUin, string targetName) => new(groupUin, targetName); + + public static GroupRenameEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetAdminEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetAdminEvent.cs new file mode 100644 index 0000000..6fb3103 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetAdminEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupSetAdminEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string Uid { get; set; } = ""; + + public bool IsAdmin { get; set; } + + private GroupSetAdminEvent(uint groupUin, string uid, bool isAdmin) : base(0) + { + GroupUin = groupUin; + Uid = uid; + IsAdmin = isAdmin; + } + + private GroupSetAdminEvent(int resultCode) : base(resultCode) { } + + public static GroupSetAdminEvent Create(uint groupUin, string uid, bool isAdmin) => new(groupUin, uid, isAdmin); + + public static GroupSetAdminEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBotEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBotEvent.cs new file mode 100644 index 0000000..2b48bca --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBotEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupSetBotEvent : ProtocolEvent +{ + public uint BotId { get; set; } + + public uint On { get; set; } + + public uint GroupUin { get; set; } + + private GroupSetBotEvent(uint botId, uint on, uint groupuin) : base(0) + { + BotId = botId; + On = on; + GroupUin = groupuin; + } + + private GroupSetBotEvent(int resultCode) : base(resultCode) { } + + public static GroupSetBotEvent Create(uint botId, uint on, uint groupuin) => new(botId, on, groupuin); + + public static GroupSetBotEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBothdEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBothdEvent.cs new file mode 100644 index 0000000..b8a5777 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetBothdEvent.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupSetBothdEvent : ProtocolEvent +{ + public uint BotId { get; set; } + + public uint GroupUin { get; set; } + + public string? Data_1 { get; set; } + + + public string? Data_2 { get; set; } + + private GroupSetBothdEvent(uint botId, uint groupuin, string? data_1, string? data_2) : base(0) + { + BotId = botId; + GroupUin = groupuin; + Data_1 = data_1; + Data_2 = data_2; + } + + private GroupSetBothdEvent(int resultCode) : base(resultCode) { } + + public static GroupSetBothdEvent Create(uint botId, uint groupuin, string? data_1, string? data_2) => new(botId, groupuin,data_1,data_2); + + public static GroupSetBothdEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetSpecialTitleEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetSpecialTitleEvent.cs new file mode 100644 index 0000000..0737fa9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetSpecialTitleEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupSetSpecialTitleEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string TargetUid { get; } = string.Empty; + + public string Title { get; } = string.Empty; + + private GroupSetSpecialTitleEvent(uint groupUin, string targetUid, string title) : base(true) + { + GroupUin = groupUin; + TargetUid = targetUid; + Title = title; + } + + private GroupSetSpecialTitleEvent(int resultCode) : base(resultCode) { } + + public static GroupSetSpecialTitleEvent Create(uint groupUin, string targetUid, string title) + => new(groupUin, targetUid, title); + + public static GroupSetSpecialTitleEvent Result(int resultCode) + => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetTodoEvent.cs new file mode 100644 index 0000000..e7b8194 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupSetTodoEvent.cs @@ -0,0 +1,24 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupSetTodoEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint Sequence { get; set; } + + public string? ResultMessage { get; set; } + + private GroupSetTodoEvent(uint groupUin, uint sequence) : base(0) + { + GroupUin = groupUin; + Sequence = sequence; + } + + private GroupSetTodoEvent(int resultCode, string? message) : base(resultCode) { + ResultMessage = message; + } + + public static GroupSetTodoEvent Create(uint groupUin, uint sequence) => new(groupUin, sequence); + + public static GroupSetTodoEvent Result(int resultCode, string? message) => new(resultCode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupTransferEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupTransferEvent.cs new file mode 100644 index 0000000..55acc83 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/GroupTransferEvent.cs @@ -0,0 +1,24 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class GroupTransferEvent : ProtocolEvent +{ + public uint GroupUin { get; } + public string SourceUid { get; } + public string TargetUid { get; } + + private GroupTransferEvent(uint groupUin, string sourceUid, string targetUid) : base(true) + { + GroupUin = groupUin; + SourceUid = sourceUid; + TargetUid = targetUid; + } + + private GroupTransferEvent(int resultCode) : base(resultCode) { } + + public static GroupTransferEvent Create(uint groupUin, string sourceUid, string targetUid) + => new(groupUin, sourceUid, targetUid); + + public static GroupTransferEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs new file mode 100644 index 0000000..c41c046 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs @@ -0,0 +1,25 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class ImageOcrEvent : ProtocolEvent +{ + public string Url { get; } = string.Empty; + + public ImageOcrResult ImageOcrResult { get; } + + private ImageOcrEvent(string url) : base(true) + { + Url = url; + ImageOcrResult = new ImageOcrResult(new List(), ""); + } + + private ImageOcrEvent(int resultCode, ImageOcrResult result) : base(resultCode) + { + ImageOcrResult = result; + } + + public static ImageOcrEvent Create(string url) => new(url); + + public static ImageOcrEvent Result(int resultCode, ImageOcrResult result) => new(resultCode, result); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/JoinEmojiChainEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/JoinEmojiChainEvent.cs new file mode 100644 index 0000000..7f0c0d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/JoinEmojiChainEvent.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class JoinEmojiChainEvent : ProtocolEvent +{ + public uint TargetMessageSeq { get; set; } + + public uint TargetFaceId { get; set; } + + public uint? GroupUin { get; set; } + + public string? FriendUid { get; set; } + + protected JoinEmojiChainEvent(uint targetMessageSeq, uint targetFaceId) : base(true) + { + TargetMessageSeq = targetMessageSeq; + TargetFaceId = targetFaceId; + } + + protected JoinEmojiChainEvent(int resultCode) : base(resultCode) { } + + public static JoinEmojiChainEvent Result(int resultCode) => new(resultCode); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RenameMemberEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RenameMemberEvent.cs new file mode 100644 index 0000000..bd39940 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RenameMemberEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +#pragma warning disable CS8618 + +internal class RenameMemberEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string TargetUid { get; } + + public string TargetName { get; } + + private RenameMemberEvent(uint groupUin, string targetUid, string targetName) : base(true) + { + GroupUin = groupUin; + TargetUid = targetUid; + TargetName = targetName; + } + + private RenameMemberEvent(int resultCode) : base(resultCode) { } + + public static RenameMemberEvent Create(uint groupUin, string targetUid, string targetName) => new(groupUin, targetUid, targetName); + + public static RenameMemberEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendEvent.cs new file mode 100644 index 0000000..1aef5eb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class RequestFriendEvent : ProtocolEvent +{ + public uint TargetUin { get; set; } + + public string Question { get; set; } = ""; + + public string Message { get; set; } = ""; + + protected RequestFriendEvent(uint targetUin, string question, string message) : base(true) + { + TargetUin = targetUin; + Question = question; + Message = message; + } + + protected RequestFriendEvent(int resultCode) : base(resultCode) + { + } + + public static RequestFriendEvent Create(uint targetUin, string question, string message) => new(targetUin, question, message); + + public static RequestFriendEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSearchEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSearchEvent.cs new file mode 100644 index 0000000..589f69b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSearchEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class RequestFriendSearchEvent : ProtocolEvent +{ + public uint TargetUin { get; } + + private RequestFriendSearchEvent(uint targetUin) : base(true) + { + TargetUin = targetUin; + } + + private RequestFriendSearchEvent(int resultCode) : base(resultCode) + { + } + + public static RequestFriendSearchEvent Create(uint targetUin) => new(targetUin); + + public static RequestFriendSearchEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSettingEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSettingEvent.cs new file mode 100644 index 0000000..dc3c24b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/RequestFriendSettingEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class RequestFriendSettingEvent : ProtocolEvent +{ + public uint TargetUin { get; set; } + + private RequestFriendSettingEvent(uint targetUin) : base(true) + { + TargetUin = targetUin; + } + + private RequestFriendSettingEvent(int resultCode) : base(resultCode) { } + + public static RequestFriendSettingEvent Create(uint targetUin) => new(targetUin); + + public static RequestFriendSettingEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetCustomStatusEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetCustomStatusEvent.cs new file mode 100644 index 0000000..7954692 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetCustomStatusEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetCustomStatusEvent : SetStatusEvent +{ + public uint FaceId { get; } + + public string Text { get; } = string.Empty; + + private SetCustomStatusEvent(uint faceId, string text) : base(2000, 0) + { + FaceId = faceId; + Text = text; + } + + private SetCustomStatusEvent(int resultCode) : base(resultCode) { } + + public static SetCustomStatusEvent Create(uint faceId, string text) => new(faceId, text); + + public new static SetCustomStatusEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupFilteredRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupFilteredRequestEvent.cs new file mode 100644 index 0000000..b4968b9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupFilteredRequestEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetGroupFilteredRequestEvent : ProtocolEvent +{ + public ulong Sequence { get; set; } + + public uint GroupUin { get; set; } + + public bool Accept { get; set; } + + public uint Type { get; } + + public string Reason { get; set; } = string.Empty; + + private SetGroupFilteredRequestEvent(bool accept, uint groupUin, ulong sequence, uint type, string? reason) : base(true) + { + Accept = accept; + GroupUin = groupUin; + Sequence = sequence; + Type = type; + Reason = reason ?? ""; + } + + private SetGroupFilteredRequestEvent(int resultCode) : base(resultCode) { } + + public static SetGroupFilteredRequestEvent Create(bool accept, uint groupUin, ulong sequence, uint type, string? reason) + => new(accept, groupUin, sequence, type, reason); + + public static SetGroupFilteredRequestEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupRequestEvent.cs new file mode 100644 index 0000000..7d5d03a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetGroupRequestEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetGroupRequestEvent : ProtocolEvent +{ + public ulong Sequence { get; set; } + + public uint GroupUin { get; set; } + + public bool Accept { get; set; } + + public uint Type { get; } + + public string Reason { get; set; } = string.Empty; + + private SetGroupRequestEvent(bool accept, uint groupUin, ulong sequence, uint type, string? reason) : base(true) + { + Accept = accept; + GroupUin = groupUin; + Sequence = sequence; + Type = type; + Reason = reason ?? ""; + } + + private SetGroupRequestEvent(int resultCode) : base(resultCode) { } + + public static SetGroupRequestEvent Create(bool accept, uint groupUin, ulong sequence, uint type, string? reason) + => new(accept, groupUin, sequence, type, reason); + + public static SetGroupRequestEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinFriendEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinFriendEvent.cs new file mode 100644 index 0000000..3595545 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinFriendEvent.cs @@ -0,0 +1,31 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetPinFriendEvent : ProtocolEvent +{ + internal string Uid { get; set; } + + public uint Uin { get; set; } + + public bool IsPin { get; set; } + + public string Message { get; set; } + + protected SetPinFriendEvent(uint uin, bool isPin) : base(true) + { + Uid = string.Empty; + Uin = uin; + Message = string.Empty; + IsPin = isPin; + } + + protected SetPinFriendEvent(int retcode, string message) : base(retcode) + { + Uid = string.Empty; + Uin = 0; + Message = message; + } + + public static SetPinFriendEvent Create(uint uin, bool isPin) => new(uin, isPin); + + public static SetPinFriendEvent Result(int retcode, string message) => new(retcode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinGroupEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinGroupEvent.cs new file mode 100644 index 0000000..de31687 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetPinGroupEvent.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetPinGroupEvent : ProtocolEvent +{ + public uint Uin { get; set; } + + public bool IsPin { get; set; } + + public string Message { get; set; } + + protected SetPinGroupEvent(uint uin, bool isPin) : base(true) + { + Uin = uin; + Message = string.Empty; + IsPin = isPin; + } + + protected SetPinGroupEvent(int retcode, string message) : base(retcode) + { + Uin = 0; + Message = message; + } + + public static SetPinGroupEvent Create(uint uin, bool isPin) => new(uin, isPin); + + public static SetPinGroupEvent Result(int retcode, string message) => new(retcode, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetStatusEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetStatusEvent.cs new file mode 100644 index 0000000..3cc30e0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Action/SetStatusEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Action; + +internal class SetStatusEvent : ProtocolEvent +{ + public uint Status { get; } + + public uint ExtStatus { get; } + + protected SetStatusEvent(uint status, uint extStatus) : base(true) + { + Status = status; + ExtStatus = extStatus; + } + + protected SetStatusEvent(int resultCode) : base(resultCode) { } + + public static SetStatusEvent Create(uint status, uint extStatus) => new(status, extStatus); + + public static SetStatusEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/EasyLoginEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/EasyLoginEvent.cs new file mode 100644 index 0000000..4ebf568 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/EasyLoginEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class EasyLoginEvent : ProtocolEvent +{ + private EasyLoginEvent() : base(true) { } + + private EasyLoginEvent(int result) : base(result) { } + + public static EasyLoginEvent Create() => new(); + + public static EasyLoginEvent Result(int result) => new(result); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/KeyExchangeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/KeyExchangeEvent.cs new file mode 100644 index 0000000..4905cc0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/KeyExchangeEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class KeyExchangeEvent : ProtocolEvent +{ + private KeyExchangeEvent() : base(true) + { + } + + private KeyExchangeEvent(int resultCode) : base(resultCode) + { + } + + public static KeyExchangeEvent Create() + { + return new KeyExchangeEvent(0); + } + + + public static KeyExchangeEvent Result() + { + return new KeyExchangeEvent(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginCommon.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginCommon.cs new file mode 100644 index 0000000..aae8ec2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginCommon.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class LoginCommon +{ + public enum Error : uint + { + TokenExpired = 140022015, + UnusualVerify = 140022011, + NewDeviceVerify = 140022010, + CaptchaVerify = 140022008, + Success = 0, + Unknown = 1, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginEvent.cs new file mode 100644 index 0000000..59042f0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/LoginEvent.cs @@ -0,0 +1,40 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Login; + +internal class LoginEvent : ProtocolEvent +{ + public byte Age { get; } + public byte Sex { get; } + public string Name { get; } + public string Tag { get; } = string.Empty; + + public string Message { get; } = string.Empty; + + private LoginEvent() : base(true) { } + + private LoginEvent(int resultCode) : base(resultCode) { } + + private LoginEvent(int resultCode, byte age, byte sex, string name) : base(resultCode) + { + Age = age; + Sex = sex; + Name = name; + } + + private LoginEvent(int resultCode, string tag, string message) : base(resultCode) + { + Tag = tag; + Message = message; + } + + public static LoginEvent Create() => new(); + + public static LoginEvent Result(int resultCode, byte age, byte sex, string name) + => new(resultCode, age, sex, name); + + public static LoginEvent Result(int resultCode) => new(resultCode); + + + public static LoginEvent Result(int resultCode, string tag, string message) => new(resultCode, tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/NewDeviceLoginEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/NewDeviceLoginEvent.cs new file mode 100644 index 0000000..0ba1f9e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/NewDeviceLoginEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class NewDeviceLoginEvent : ProtocolEvent +{ + public bool Success { get; set; } + + private NewDeviceLoginEvent() : base(true) { } + + private NewDeviceLoginEvent(int result) : base(result) + { + Success = result == 0; + } + + public static NewDeviceLoginEvent Create() => new(); + + public static NewDeviceLoginEvent Result(int result) => new(result); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/PasswordLoginEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/PasswordLoginEvent.cs new file mode 100644 index 0000000..acb8973 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/PasswordLoginEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class PasswordLoginEvent : ProtocolEvent +{ + public string? Tag { get; set; } + + public string? Message { get; set; } + + private PasswordLoginEvent() : base(true) { } + + private PasswordLoginEvent(int result, string? tag = null, string? message = null) : base(result) + { + Tag = tag; + Message = message; + } + + public static PasswordLoginEvent Create() => new(); + + public static PasswordLoginEvent Result(int result, string? tag = null, string? message = null) => new(result, tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/TransEmpEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/TransEmpEvent.cs new file mode 100644 index 0000000..c24b048 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/TransEmpEvent.cs @@ -0,0 +1,69 @@ +using Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; + +#pragma warning disable CS8618 +namespace Lagrange.Core.Internal.Event.Login; + +internal class TransEmpEvent : ProtocolEvent +{ + public State EventState { get; set; } + + #region TransEmp CMD0x31 + + public byte[] QrCode { get; } + + public uint Expiration { get; } + + public string Url { get; } + + public string QrSig { get; } + + public byte[] Signature { get; } + + #endregion + + #region TransEmp CMD0x12 + + public byte[]? TgtgtKey { get; } + + public byte[]? TempPassword { get; } + + public byte[]? NoPicSig { get; } + + #endregion + + private TransEmpEvent(State eventState) : base(true) => EventState = eventState; + + private TransEmpEvent(int result, byte[] qrCode, uint expiration, string url, string qrSig, byte[] signature) + : base(result) + { + EventState = State.FetchQrCode; + + QrCode = qrCode; + Expiration = expiration; + Url = url; + QrSig = qrSig; + Signature = signature; + } + + private TransEmpEvent(int result, byte[]? tgtgtKey, byte[]? tempPassword, byte[]? noPicSig) : base(result) + { + TgtgtKey = tgtgtKey; + TempPassword = tempPassword; + NoPicSig = noPicSig; + EventState = State.QueryResult; + } + + public static TransEmpEvent Create(State eventState) => new(eventState); + + public static TransEmpEvent Result(byte[] qrCode, uint expiration, string url, string qrSig, byte[] signature) => + new(0, qrCode, expiration, url, qrSig, signature); + + public static TransEmpEvent Result(TransEmp12.State state, byte[]? tgtgtKey, byte[]? tempPassword, byte[]? noPicSig) => + new((int)state, tgtgtKey, tempPassword, noPicSig); + + public enum State : byte + { + FetchQrCode = 0x31, + QueryResult = 0x12 + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Login/UnusualEasyLoginEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/UnusualEasyLoginEvent.cs new file mode 100644 index 0000000..6946448 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Login/UnusualEasyLoginEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Login; + +internal class UnusualEasyLoginEvent : ProtocolEvent +{ + public bool Success { get; set; } + + private UnusualEasyLoginEvent() : base(true) { } + + private UnusualEasyLoginEvent(int result) : base(result) + { + Success = result == 0; + } + + public static UnusualEasyLoginEvent Create() + { + return new UnusualEasyLoginEvent(); + } + + public static UnusualEasyLoginEvent Result(int result) + { + return new UnusualEasyLoginEvent(result); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileDownloadEvent.cs new file mode 100644 index 0000000..cd5733e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileDownloadEvent.cs @@ -0,0 +1,32 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class FileDownloadEvent : ProtocolEvent +{ + public string? SenderUid { get; set; } + + public string? ReceiverUid { get; set; } + + public string? FileUuid { get; set; } + + public string? FileHash { get; set; } + + public string? FileUrl { get; set; } + + private FileDownloadEvent(string fileUuid, string fileHash, string? senderUid, string? receiverUid) : base(0) + { + FileUuid = fileUuid; + FileHash = fileHash; + SenderUid = senderUid; + ReceiverUid = receiverUid; + } + + private FileDownloadEvent(int resultCode, string fileUrl) : base(resultCode) + { + FileUrl = fileUrl; + } + + public static FileDownloadEvent Create(string fileUuid, string fileHash, string? senderUid, string? receiverUid) => + new(fileUuid, fileHash, senderUid, receiverUid); + + public static FileDownloadEvent Result(int resultCode, string fileUrl) => new(resultCode, fileUrl); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileUploadEvent.cs new file mode 100644 index 0000000..c7ee9f4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FileUploadEvent.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Message.Entity; + +namespace Lagrange.Core.Internal.Event.Message; + +#pragma warning disable CS8618 + +internal class FileUploadEvent : ProtocolEvent +{ + public string TargetUid { get; } + + public FileEntity Entity { get; } + + public bool IsExist { get; } + + public string FileId { get; } + + public byte[] UploadKey { get; } + + public string Ip { get; } + + public uint Port { get; } + + public string Addon { get; } + + private FileUploadEvent(string targetUid, FileEntity entity) : base(true) + { + TargetUid = targetUid; + Entity = entity; + } + + private FileUploadEvent(int resultCode, bool isExist, string fileId, byte[] uploadKey, string ip, uint port, string addon) : base(resultCode) + { + IsExist = isExist; + FileId = fileId; + UploadKey = uploadKey; + Ip = ip; + Port = port; + Addon = addon; + } + + public static FileUploadEvent Create(string targetUid, FileEntity entity) => new(targetUid, entity); + + public static FileUploadEvent Result(int resultCode, bool isExist, string fileId, byte[] uploadKey, string ip, uint port, string addon) + => new(resultCode, isExist, fileId, uploadKey, ip, port, addon); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FriendPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FriendPokeEvent.cs new file mode 100644 index 0000000..8b748cc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/FriendPokeEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class FriendPokeEvent : ProtocolEvent +{ + public uint FriendUin { get; } + + protected FriendPokeEvent(uint friendUin) : base(true) + { + FriendUin = friendUin; + } + + protected FriendPokeEvent(int resultCode) : base(resultCode) { } + + public static FriendPokeEvent Create(uint friendUin) => new(friendUin); + + public static FriendPokeEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetC2cMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetC2cMessageEvent.cs new file mode 100644 index 0000000..8133df1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetC2cMessageEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class GetC2cMessageEvent : ProtocolEvent +{ + public string FriendUid { get; } = string.Empty; + + public uint StartSequence { get; } + + public uint EndSequence { get; } + + public List Chains { get; } = new(); + + private GetC2cMessageEvent(string friendUid, uint startSequence, uint endSequence) : base(true) + { + FriendUid = friendUid; + StartSequence = startSequence; + EndSequence = endSequence; + } + + private GetC2cMessageEvent(List chains) : base(0) + { + Chains = chains; + } + + public static GetC2cMessageEvent Create(string friendUid, uint startSequence, uint endSequence) + => new(friendUid, startSequence, endSequence); + + public static GetC2cMessageEvent Result(List chains) + => new(chains); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetGroupMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetGroupMessageEvent.cs new file mode 100644 index 0000000..47fe65d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetGroupMessageEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class GetGroupMessageEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint StartSequence { get; } + + public uint EndSequence { get; } + + public List Chains { get; } = new(); + + private GetGroupMessageEvent(uint groupUin, uint startSequence, uint endSequence) : base(true) + { + GroupUin = groupUin; + StartSequence = startSequence; + EndSequence = endSequence; + } + + private GetGroupMessageEvent(int resultCode, List chains) : base(resultCode) + { + Chains = chains; + } + + public static GetGroupMessageEvent Create(uint groupUin, uint startSequence, uint endSequence) + => new(groupUin, startSequence, endSequence); + + public static GetGroupMessageEvent Result(int resultCode, List chains) + => new(resultCode, chains); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetLatestGroupMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetLatestGroupMessageEvent.cs new file mode 100644 index 0000000..69c8009 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetLatestGroupMessageEvent.cs @@ -0,0 +1,31 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class GetGroupInfoEvent : ProtocolEvent +{ + public ulong Uin { get; } + + public string? Message { get; } + + public BotGroupInfo Info { get; } + + protected GetGroupInfoEvent(ulong uin) : base(true) + { + Uin = uin; + Info = new(); + } + + protected GetGroupInfoEvent(int code, string? message, BotGroupInfo info) : base(code) + { + Message = message; + Info = info; + } + + public static GetGroupInfoEvent Create(ulong uin) => new(uin); + + public static GetGroupInfoEvent Result(int code, string? message, BotGroupInfo info) + { + return new(code, message, info); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetRoamMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetRoamMessageEvent.cs new file mode 100644 index 0000000..437defc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GetRoamMessageEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class GetRoamMessageEvent : ProtocolEvent +{ + public string FriendUid { get; } = string.Empty; + + public uint Time { get; } + + public uint Count { get; } + + public List Chains { get; } = new(); + + private GetRoamMessageEvent(string friendUid, uint time, uint count) : base(true) + { + FriendUid = friendUid; + Time = time; + Count = count; + } + + private GetRoamMessageEvent(int resultCode, List chains) : base(resultCode) + { + Chains = chains; + } + + public static GetRoamMessageEvent Create(string friendUid, uint time, uint count) + => new(friendUid, time, count); + + public static GetRoamMessageEvent Result(int resultCode, List chains) + => new(resultCode, chains); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupFSUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupFSUploadEvent.cs new file mode 100644 index 0000000..e2aea20 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupFSUploadEvent.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class GroupFSUploadEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string TargetDirectory { get; } + + public FileEntity Entity { get; } + + public bool IsExist { get; } + + public string FileId { get; } + + public byte[] UploadKey { get; } + + public byte[] CheckKey { get; } + + public string Ip { get; } + + public uint Port { get; } + + private GroupFSUploadEvent(uint groupUin, string targetDirectory, FileEntity entity) : base(true) + { + GroupUin = groupUin; + TargetDirectory = targetDirectory; + Entity = entity; + } + + private GroupFSUploadEvent(int resultCode, bool isExist, string fileId, byte[] uploadKey, byte[] checkKey, string ip, uint port) : base(resultCode) + { + IsExist = isExist; + FileId = fileId; + UploadKey = uploadKey; + CheckKey = checkKey; + Ip = ip; + Port = port; + } + + public static GroupFSUploadEvent Create(uint groupUin, string targetDirectory, FileEntity entity) + => new(groupUin, targetDirectory, entity); + + public static GroupFSUploadEvent Result(int resultCode, bool isExist, string fileId, byte[] uploadKey, byte[] checkKey, string ip, uint port) + => new(resultCode, isExist, fileId, uploadKey, checkKey, ip, port); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupPokeEvent.cs new file mode 100644 index 0000000..eefed14 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupPokeEvent.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class GroupPokeEvent : FriendPokeEvent +{ + public uint GroupUin { get; } + + private GroupPokeEvent(uint friendUin, uint groupUin) : base(friendUin) + { + GroupUin = groupUin; + } + + private GroupPokeEvent(int resultCode) : base(resultCode) { } + + public static GroupPokeEvent Create(uint friendUin, uint groupUin) => new(friendUin, groupUin); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupSendFileEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupSendFileEvent.cs new file mode 100644 index 0000000..97f5024 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/GroupSendFileEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class GroupSendFileEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string FileKey { get; } = string.Empty; + + private GroupSendFileEvent(uint groupUin, string fileKey) : base(true) + { + GroupUin = groupUin; + FileKey = fileKey; + } + + private GroupSendFileEvent(int resultCode) : base(resultCode) { } + + public static GroupSendFileEvent Create(uint groupUin, string fileKey) => new(groupUin, fileKey); + + public static GroupSendFileEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageDownloadEvent.cs new file mode 100644 index 0000000..1c1b919 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageDownloadEvent.cs @@ -0,0 +1,29 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Message; + +#pragma warning disable CS8618 + +internal class ImageDownloadEvent : ProtocolEvent +{ + public string SelfUid { get; } + + public IndexNode Node { get; } + + public string ImageUrl { get; } + + protected ImageDownloadEvent(string selfUid, MsgInfo info) : base(true) + { + SelfUid = selfUid; + Node = info.MsgInfoBody[0].Index; + } + + protected ImageDownloadEvent(int resultCode, string imageUrl) : base(resultCode) + { + ImageUrl = imageUrl; + } + + public static ImageDownloadEvent Create(string selfUid, MsgInfo info) => new(selfUid, info); + + public static ImageDownloadEvent Result(int resultCode, string url) => new(resultCode, url); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupDownloadEvent.cs new file mode 100644 index 0000000..b5d986a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupDownloadEvent.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class ImageGroupDownloadEvent : ImageDownloadEvent +{ + public uint GroupUin { get; } + + private ImageGroupDownloadEvent(uint groupUin, MsgInfo info) : base("", info) + { + GroupUin = groupUin; + } + + private ImageGroupDownloadEvent(int resultCode, string imageUrl) : base(resultCode, imageUrl) { } + + public static ImageGroupDownloadEvent Create(uint groupUin, MsgInfo info) => new(groupUin, info); + + public new static ImageGroupDownloadEvent Result(int resultCode, string url) => new(resultCode, url); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupUploadEvent.cs new file mode 100644 index 0000000..6927fd5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageGroupUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class ImageGroupUploadEvent : NTV2RichMediaUploadEvent +{ + public ImageEntity Entity { get; } + + public uint GroupUin { get; } + + public CustomFace Compat { get; } + + private ImageGroupUploadEvent(ImageEntity entity, uint groupUin) + { + Entity = entity; + GroupUin = groupUin; + } + + private ImageGroupUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, CustomFace compat) + : base(resultCode, msgInfo, uKey, network, subFiles) + { + Compat = compat; + } + + public static ImageGroupUploadEvent Create(ImageEntity entity, uint groupUin) + => new(entity, groupUin); + + public static ImageGroupUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, CustomFace compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageUploadEvent.cs new file mode 100644 index 0000000..805b8b0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/ImageUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class ImageUploadEvent : NTV2RichMediaUploadEvent +{ + public ImageEntity Entity { get; } + + public string TargetUid { get; set; } + + public NotOnlineImage Compat { get; } + + private ImageUploadEvent(ImageEntity entity, string targetUid) + { + Entity = entity; + TargetUid = targetUid; + } + + private ImageUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, NotOnlineImage compat) + : base(resultCode, msgInfo, uKey, network, subFiles) + { + Compat = compat; + } + + public static ImageUploadEvent Create(ImageEntity entity, string targetUid) + => new(entity, targetUid); + + public static ImageUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, NotOnlineImage compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MarkReadedEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MarkReadedEvent.cs new file mode 100644 index 0000000..1a56676 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MarkReadedEvent.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class MarkReadedEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string? TargetUid { get; } + + public uint StartSequence { get; } + + public uint Time { get; } + + private MarkReadedEvent(uint groupUin, string? targetUid, uint startSequence, uint time) : base(true) + { + GroupUin = groupUin; + TargetUid = targetUid; + StartSequence = startSequence; + Time = time; + } + + private MarkReadedEvent(int resultCode) : base(resultCode) { } + + public static MarkReadedEvent Create(uint groupUin, string? targetUid, uint startSequence, uint time) => + new(groupUin, targetUid, startSequence, time); + + public static MarkReadedEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgDownloadEvent.cs new file mode 100644 index 0000000..f58a524 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgDownloadEvent.cs @@ -0,0 +1,27 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class MultiMsgDownloadEvent : ProtocolEvent +{ + public string? Uid { get; } + + public string? ResId { get; } + + public List? Chains { get; } + + private MultiMsgDownloadEvent(string uid, string resId) : base(true) + { + Uid = uid; + ResId = resId; + } + + private MultiMsgDownloadEvent(int resultCode, List chains) : base(resultCode) + { + Chains = chains; + } + + public static MultiMsgDownloadEvent Create(string uid, string resId) => new(uid, resId); + + public static MultiMsgDownloadEvent Result(int resultCode, List chains) => new(resultCode, chains); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgUploadEvent.cs new file mode 100644 index 0000000..eecb19f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/MultiMsgUploadEvent.cs @@ -0,0 +1,27 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class MultiMsgUploadEvent : ProtocolEvent +{ + public uint? GroupUin { get; } + + public string? ResId { get; } + + public List? Chains { get; } + + private MultiMsgUploadEvent(int resultCode, string resId) : base(resultCode) + { + ResId = resId; + } + + private MultiMsgUploadEvent(uint? groupUin, List chains) : base(true) + { + GroupUin = groupUin; + Chains = chains; + } + + public static MultiMsgUploadEvent Create(uint? groupUin, List chains) => new(groupUin, chains); + + public static MultiMsgUploadEvent Result(int resultCode, string resId) => new(resultCode, resId); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/NTV2RichMediaUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/NTV2RichMediaUploadEvent.cs new file mode 100644 index 0000000..3b9aaab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/NTV2RichMediaUploadEvent.cs @@ -0,0 +1,26 @@ +#pragma warning disable CS8618 + +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Message; + +internal abstract class NTV2RichMediaUploadEvent : ProtocolEvent +{ + public MsgInfo MsgInfo { get; } + + public string? UKey { get; } + + public List Network { get; } + + public List SubFiles { get; } + + protected NTV2RichMediaUploadEvent() : base(true) { } + + protected NTV2RichMediaUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles) : base(resultCode) + { + MsgInfo = msgInfo; + UKey = uKey; + Network = network; + SubFiles = subFiles; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/PushMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/PushMessageEvent.cs new file mode 100644 index 0000000..65a0a5d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/PushMessageEvent.cs @@ -0,0 +1,15 @@ +using Lagrange.Core.Message; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class PushMessageEvent : ProtocolEvent +{ + public MessageChain Chain { get; set; } + + private PushMessageEvent(int resultCode, MessageChain chain) : base(resultCode) + { + Chain = chain; + } + + public static PushMessageEvent Create(MessageChain chain) => new(0, chain); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallFriendMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallFriendMessageEvent.cs new file mode 100644 index 0000000..f084fd2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallFriendMessageEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class RecallFriendMessageEvent : ProtocolEvent +{ + public string TargetUid { get; } = string.Empty; + + public uint ClientSeq { get; } + + public uint MessageSeq { get; } + + public uint Random { get; } + + public uint Timestamp { get; } + + private RecallFriendMessageEvent(string targetUid, uint clientSeq, uint messageSeq, uint random, uint timestamp) : base(true) + { + TargetUid = targetUid; + MessageSeq = messageSeq; + ClientSeq = clientSeq; + Random = random; + Timestamp = timestamp; + } + + private RecallFriendMessageEvent(int resultCode) : base(resultCode) { } + + public static RecallFriendMessageEvent Create(string targetUid, uint clientSeq, uint messageSeq, uint random, uint timestamp) => + new(targetUid, clientSeq, messageSeq, random, timestamp); + + public static RecallFriendMessageEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallGroupMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallGroupMessageEvent.cs new file mode 100644 index 0000000..6a4c24d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecallGroupMessageEvent.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class RecallGroupMessageEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint Sequence { get; set; } + + private RecallGroupMessageEvent(uint sequence, uint groupUin) : base(true) + { + Sequence = sequence; + GroupUin = groupUin; + } + + private RecallGroupMessageEvent(int resultCode) : base(resultCode) { } + + public static RecallGroupMessageEvent Create(uint groupUin, uint sequence) => + new(sequence, groupUin); + + public static RecallGroupMessageEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordDownloadEvent.cs new file mode 100644 index 0000000..fea239a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordDownloadEvent.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Message; + +#pragma warning disable CS8618 + +internal class RecordDownloadEvent : ProtocolEvent +{ + public string SelfUid { get; } + + public string FileUuid { get; } + + public IndexNode? Node { get; } + + public string AudioUrl { get; } + + protected RecordDownloadEvent(string selfUid, MsgInfo info) : base(true) + { + SelfUid = selfUid; + Node = info.MsgInfoBody[0].Index; + } + + protected RecordDownloadEvent(string selfUid, string fileUuid) : base(true) + { + SelfUid = selfUid; + FileUuid = fileUuid; + } + + protected RecordDownloadEvent(int resultCode, string audioUrl) : base(resultCode) + { + AudioUrl = audioUrl; + } + + public static RecordDownloadEvent Create(string selfUid, MsgInfo info) => new(selfUid, info); + + public static RecordDownloadEvent Create(string selfUid, string fileUuid) => new(selfUid, fileUuid); + + public static RecordDownloadEvent Result(int resultCode, string url) => new(resultCode, url); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupDownloadEvent.cs new file mode 100644 index 0000000..c4ca9f1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupDownloadEvent.cs @@ -0,0 +1,26 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.Message; + +internal class RecordGroupDownloadEvent : RecordDownloadEvent +{ + public uint GroupUin { get; } + + private RecordGroupDownloadEvent(uint groupUin, MsgInfo info) : base("", info) + { + GroupUin = groupUin; + } + + private RecordGroupDownloadEvent(uint groupUin, string fileUuid) : base("", fileUuid) + { + GroupUin = groupUin; + } + + private RecordGroupDownloadEvent(int resultCode, string audioUrl) : base(resultCode, audioUrl) { } + + public static RecordGroupDownloadEvent Create(uint groupUin, MsgInfo info) => new(groupUin, info); + + public static RecordGroupDownloadEvent Create(uint groupUin, string fileUuid) => new(groupUin, fileUuid); + + public new static RecordGroupDownloadEvent Result(int resultCode, string url) => new(resultCode, url); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupUploadEvent.cs new file mode 100644 index 0000000..8ffece3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordGroupUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Component; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class RecordGroupUploadEvent : NTV2RichMediaUploadEvent +{ + public RecordEntity Entity { get; } + + public uint GroupUin { get; } + + public RichText Compat { get; } + + private RecordGroupUploadEvent(RecordEntity entity, uint groupUin) + { + Entity = entity; + GroupUin = groupUin; + } + + private RecordGroupUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, RichText compat) + : base(resultCode, msgInfo, uKey, network, subFiles) + { + Compat = compat; + } + + public static RecordGroupUploadEvent Create(RecordEntity entity, uint groupUin) + => new(entity, groupUin); + + public static RecordGroupUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, RichText compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordUploadEvent.cs new file mode 100644 index 0000000..d03b46e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RecordUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Component; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class RecordUploadEvent : NTV2RichMediaUploadEvent +{ + public RecordEntity Entity { get; } + + public string TargetUid { get; set; } + + public RichText Compat { get; } + + private RecordUploadEvent(RecordEntity entity, string targetUid) + { + Entity = entity; + TargetUid = targetUid; + } + + private RecordUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, RichText compat) + : base(resultCode, msgInfo, uKey, network, subFiles) + { + Compat = compat; + } + + public static RecordUploadEvent Create(RecordEntity entity, string targetUid) + => new(entity, targetUid); + + public static RecordUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, RichText compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RemoveEssenceMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RemoveEssenceMessageEvent.cs new file mode 100644 index 0000000..cb4b7c2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/RemoveEssenceMessageEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class RemoveEssenceMessageEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public uint Random { get; } + + private RemoveEssenceMessageEvent(uint groupUin, uint sequence, uint random) : base(true) + { + GroupUin = groupUin; + Sequence = sequence; + Random = random; + } + + private RemoveEssenceMessageEvent(int resultCode) : base(resultCode) { } + + public static RemoveEssenceMessageEvent Create(uint groupUin, uint sequence, uint random) + => new(groupUin, sequence, random); + + public static RemoveEssenceMessageEvent Result(int resultCode) + => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SendMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SendMessageEvent.cs new file mode 100644 index 0000000..981d386 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SendMessageEvent.cs @@ -0,0 +1,26 @@ +using Lagrange.Core.Message; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class SendMessageEvent : ProtocolEvent +{ + public readonly MessageChain Chain; + + public readonly MessageResult MsgResult; + + protected SendMessageEvent(MessageChain chain) : base(true) + { + Chain = chain; + } + + protected SendMessageEvent(int resultCode, MessageResult msgResult) : base(resultCode) + { + MsgResult = msgResult; + } + + public static SendMessageEvent Create(MessageChain chain) => new(chain); + + public static SendMessageEvent Result(int resultCode, MessageResult msgResult) => new(resultCode, msgResult); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SetEssenceMessageEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SetEssenceMessageEvent.cs new file mode 100644 index 0000000..36cb142 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/SetEssenceMessageEvent.cs @@ -0,0 +1,25 @@ +namespace Lagrange.Core.Internal.Event.Message; + +internal class SetEssenceMessageEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public uint Random { get; } + + private SetEssenceMessageEvent(uint groupUin, uint sequence, uint random) : base(true) + { + GroupUin = groupUin; + Sequence = sequence; + Random = random; + } + + private SetEssenceMessageEvent(int resultCode) : base(resultCode) { } + + public static SetEssenceMessageEvent Create(uint groupUin, uint sequence, uint random) + => new(groupUin, sequence, random); + + public static SetEssenceMessageEvent Result(int resultCode) + => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoDownloadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoDownloadEvent.cs new file mode 100644 index 0000000..c59cc88 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoDownloadEvent.cs @@ -0,0 +1,40 @@ +namespace Lagrange.Core.Internal.Event.Message; + +#pragma warning disable CS8618 + +internal class VideoDownloadEvent : ProtocolEvent +{ + public string AudioUrl { get; } + + public string Uuid { get; } + + public string SelfUid { get; } + + public string FileName { get; } + + public string FileMd5 { get; } + + public string? FileSha1 { get; } + + public bool IsGroup { get; } + + private VideoDownloadEvent(string uuid, string selfUid, string fileName, string fileMd5, string? fileSha1, bool isGroup) : base(true) + { + Uuid = uuid; + SelfUid = selfUid; + FileName = fileName; + FileSha1 = fileSha1; + IsGroup = isGroup; + FileMd5 = fileMd5; + } + + private VideoDownloadEvent(int resultCode, string audioUrl) : base(resultCode) + { + AudioUrl = audioUrl; + } + + public static VideoDownloadEvent Create(string uuid, string selfUid, string fileName, string fileMd5, string? fileSha1 = null, bool isGroup = false) + => new(uuid, selfUid, fileName, fileMd5, fileSha1, isGroup); + + public static VideoDownloadEvent Result(int resultCode, string url) => new(resultCode, url); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoGroupUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoGroupUploadEvent.cs new file mode 100644 index 0000000..bbfb9af --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoGroupUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class VideoGroupUploadEvent : NTV2RichMediaUploadEvent +{ + public VideoEntity Entity { get; } + + public uint GroupUin { get; set; } + + public VideoFile Compat { get; } + + private VideoGroupUploadEvent(VideoEntity entity, uint groupUin) + { + Entity = entity; + GroupUin = groupUin; + } + + private VideoGroupUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, VideoFile compat) + : base(resultCode, msgInfo, uKey, network, subFiles) + { + Compat = compat; + } + + public static VideoGroupUploadEvent Create(VideoEntity entity, uint groupUin) + => new(entity, groupUin); + + public static VideoGroupUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, VideoFile compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoUploadEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoUploadEvent.cs new file mode 100644 index 0000000..70f256a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Message/VideoUploadEvent.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.Message; + +internal class VideoUploadEvent : NTV2RichMediaUploadEvent +{ + public VideoEntity Entity { get; } + + public string TargetUid { get; set; } + + public VideoFile Compat { get; } + + private VideoUploadEvent(VideoEntity entity, string targetUid) + { + Entity = entity; + TargetUid = targetUid; + } + + private VideoUploadEvent(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, VideoFile compat) + : base(resultCode, msgInfo, uKey, network, new List()) + { + Compat = compat; + } + + public static VideoUploadEvent Create(VideoEntity entity, string targetUid) + => new(entity, targetUid); + + public static VideoUploadEvent Result(int resultCode, MsgInfo msgInfo, string? uKey, List network, List subFiles, VideoFile compat) + => new(resultCode, msgInfo, uKey, network, subFiles, compat); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysPokeEvent.cs new file mode 100644 index 0000000..bdd8775 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysPokeEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class FriendSysPokeEvent : ProtocolEvent +{ + public uint OperatorUin { get; } + + public uint TargetUin { get; } + + public string Action { get; } + + public string Suffix { get; } + + public string ActionImgUrl { get; } + + private FriendSysPokeEvent(uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) : base(0) + { + OperatorUin = operatorUin; + TargetUin = targetUin; + Action = action; + Suffix = suffix; + ActionImgUrl = actionImgUrl; + } + + public static FriendSysPokeEvent Result(uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) + => new(operatorUin, targetUin, action, suffix, actionImgUrl); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRecallEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRecallEvent.cs new file mode 100644 index 0000000..3dd05cc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRecallEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class FriendSysRecallEvent : ProtocolEvent +{ + public string FromUid { get; } + + public uint ClientSequence { get; } + + public uint Time { get; } + + public uint Random { get; } + + public string Tip { get; } + + private FriendSysRecallEvent(string fromUid, uint clientSequence, uint time, uint random, string tip) : base(0) + { + FromUid = fromUid; + ClientSequence = clientSequence; + Time = time; + Random = random; + Tip = tip; + } + + public static FriendSysRecallEvent Result(string fromUid, uint clientSequence, uint time, uint random, string tip) + => new(fromUid, clientSequence, time, random, tip); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRequestEvent.cs new file mode 100644 index 0000000..cdb9b71 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/FriendSysRequestEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class FriendSysRequestEvent : ProtocolEvent +{ + public uint SourceUin { get; } + + public string SourceUid { get; } + + public string Message { get; } + + public string Source { get; } + + private FriendSysRequestEvent(uint sourceUin, string sourceUid, string message, string source) : base(0) + { + SourceUin = sourceUin; + SourceUid = sourceUid; + Message = message; + Source = source; + } + + public static FriendSysRequestEvent Result(uint sourceUin, string sourceUid, string message, string source) => + new(sourceUin, sourceUid, message, source); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysAdminEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysAdminEvent.cs new file mode 100644 index 0000000..8721508 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysAdminEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysAdminEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string Uid { get; } + + public bool IsPromoted { get; } + + private GroupSysAdminEvent(uint groupUin, string uid, bool isPromoted) : base(0) + { + GroupUin = groupUin; + Uid = uid; + IsPromoted = isPromoted; + } + + public static GroupSysAdminEvent Result(uint groupUin, string uid, bool isPromoted) => new(groupUin, uid, isPromoted); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysChangeName.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysChangeName.cs new file mode 100644 index 0000000..4c404c0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysChangeName.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysNameChangeEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string Name { get; } + + private GroupSysNameChangeEvent(uint groupUin, string name) : base(0) + { + GroupUin = groupUin; + Name = name; + } + + public static GroupSysNameChangeEvent Result(uint groupUin, string name) + => new(groupUin, name); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysDecreaseEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysDecreaseEvent.cs new file mode 100644 index 0000000..1fa3855 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysDecreaseEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysDecreaseEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string MemberUid { get; } + + public string? OperatorUid { get; } + + public uint Type { get; } + + private GroupSysDecreaseEvent(uint groupUin, string memberUid, string? operatorUid, uint type) : base(0) + { + GroupUin = groupUin; + MemberUid = memberUid; + OperatorUid = operatorUid; + Type = type; + } + + public static GroupSysDecreaseEvent Result(uint groupUin, string uid, string? operatorUid, uint type) => + new(groupUin, uid, operatorUid, type); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysEssenceEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysEssenceEvent.cs new file mode 100644 index 0000000..478aff7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysEssenceEvent.cs @@ -0,0 +1,29 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysEssenceEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint Sequence { get; } + + public uint Random { get; } + + public uint SetFlag { get; } // 1 设置精华消息, 2 移除精华消息 + + public uint FromUin { get; } + + public uint OperatorUin { get; } + + private GroupSysEssenceEvent(uint groupUin, uint sequence, uint random, uint setFlag, uint fromUin, uint operatorUin) : base(0) + { + GroupUin = groupUin; + Sequence = sequence; + Random = random; + SetFlag = setFlag; + FromUin = fromUin; + OperatorUin = operatorUin; + } + + public static GroupSysEssenceEvent Result(uint groupUin, uint sequence,uint random, uint setFlag, uint fromUin, uint operatorUin) + => new(groupUin, sequence, random, setFlag, fromUin, operatorUin); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysIncreaseEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysIncreaseEvent.cs new file mode 100644 index 0000000..9cabad6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysIncreaseEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysIncreaseEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string MemberUid { get; } + + public string? InvitorUid { get; } + + public uint Type { get; } + + private GroupSysIncreaseEvent(uint groupUin, string memberUid, string? invitorUid, uint type) : base(0) + { + GroupUin = groupUin; + MemberUid = memberUid; + InvitorUid = invitorUid; + Type = type; + } + + public static GroupSysIncreaseEvent Result(uint groupUin, string uid, string? invitorUid, uint type) => + new(groupUin, uid, invitorUid, type); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysInviteEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysInviteEvent.cs new file mode 100644 index 0000000..7fd88bf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysInviteEvent.cs @@ -0,0 +1,16 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysInviteEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public string InvitorUid { get; set; } + + private GroupSysInviteEvent(uint groupUin, string invitorUid) : base(0) + { + GroupUin = groupUin; + InvitorUid = invitorUid; + } + + public static GroupSysInviteEvent Result(uint groupUin, string invitorUid) => new(groupUin, invitorUid); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs new file mode 100644 index 0000000..9009151 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysMemberEnterEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint GroupMemberUin { get; } + + public uint StyleId { get; } + + private GroupSysMemberEnterEvent(uint groupUin, uint groupMemberUin, uint styleId) : base(0) + { + GroupUin = groupUin; + GroupMemberUin = groupMemberUin; + StyleId = styleId; + } + + public static GroupSysMemberEnterEvent Result(uint groupUin, uint groupMemberUin, uint styleId) => + new(groupUin, groupMemberUin, styleId); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberMuteEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberMuteEvent.cs new file mode 100644 index 0000000..3b88ead --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMemberMuteEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysMemberMuteEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string? OperatorUid { get; } + + public string TargetUid { get; } + + public uint Duration { get; } + + private GroupSysMemberMuteEvent(uint groupUin, string? operatorUid, string targetUid, uint duration) : base(0) + { + GroupUin = groupUin; + OperatorUid = operatorUid; + TargetUid = targetUid; + Duration = duration; + } + + public static GroupSysMemberMuteEvent Result(uint groupUin, string? operatorUid, string targetUid, uint duration) => + new(groupUin, operatorUid, targetUid, duration); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMuteEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMuteEvent.cs new file mode 100644 index 0000000..451e473 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysMuteEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysMuteEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string? OperatorUid { get; } + + public bool IsMuted { get; } + + private GroupSysMuteEvent(uint groupUin, string? operatorUid, bool isMuted) : base(0) + { + GroupUin = groupUin; + OperatorUid = operatorUid; + IsMuted = isMuted; + } + + public static GroupSysMuteEvent Result(uint groupUin, string? operatorUid, bool isMuted) => + new(groupUin, operatorUid, isMuted); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysPokeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysPokeEvent.cs new file mode 100644 index 0000000..cb9f48a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysPokeEvent.cs @@ -0,0 +1,29 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysPokeEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint OperatorUin { get; } + + public uint TargetUin { get; } + + public string Action { get; } + + public string Suffix { get; } + + public string ActionImgUrl { get; } + + private GroupSysPokeEvent(uint groupUin, uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) : base(0) + { + GroupUin = groupUin; + OperatorUin = operatorUin; + TargetUin = targetUin; + Action = action; + Suffix = suffix; + ActionImgUrl = actionImgUrl; + } + + public static GroupSysPokeEvent Result(uint groupUin, uint operatorUin, uint targetUin, string action, string suffix, string actionImgUrl) + => new(groupUin, operatorUin, targetUin, action, suffix, actionImgUrl); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysReaction.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysReaction.cs new file mode 100644 index 0000000..b896b03 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysReaction.cs @@ -0,0 +1,29 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysReactionEvent : ProtocolEvent +{ + public uint TargetGroupUin { get; } + + public uint TargetSequence { get; } + + public string OperatorUid { get; } + + public bool IsAdd { get; } + + public string Code { get; } + + public uint Count { get; } + + private GroupSysReactionEvent(uint targetGroupUin, uint targetSequence, string operatorUid, bool isAdd, string code,uint count) : base(0) + { + TargetGroupUin = targetGroupUin; + TargetSequence = targetSequence; + OperatorUid = operatorUid; + IsAdd = isAdd; + Code = code; + Count = count; + } + + public static GroupSysReactionEvent Result(uint groupUin, uint targetSequence, string operatorUid, bool isAdd, string code, uint count) + => new(groupUin, targetSequence, operatorUid, isAdd, code, count); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRecallEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRecallEvent.cs new file mode 100644 index 0000000..4c61245 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRecallEvent.cs @@ -0,0 +1,32 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysRecallEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string AuthorUid { get; } + + public string? OperatorUid { get; } + + public uint Sequence { get; } + + public uint Time { get; } + + public uint Random { get; } + + public string Tip { get; } + + private GroupSysRecallEvent(uint groupUin, string authorUid, string? operatorUid, uint sequence, uint time, uint random, string tip) : base(0) + { + GroupUin = groupUin; + AuthorUid = authorUid; + OperatorUid = operatorUid; + Sequence = sequence; + Time = time; + Random = random; + Tip = tip; + } + + public static GroupSysRecallEvent Result(uint groupUin, string authorUid, string? operatorUid, uint sequence, uint time, uint random, string tip) + => new(groupUin, authorUid, operatorUid, sequence, time, random, tip); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestInvitationEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestInvitationEvent.cs new file mode 100644 index 0000000..3940237 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestInvitationEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysRequestInvitationEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string TargetUid { get; } + + public string InvitorUid { get; } + + private GroupSysRequestInvitationEvent(uint groupUin, string targetUid, string invitorUid) : base(0) + { + GroupUin = groupUin; + TargetUid = targetUid; + InvitorUid = invitorUid; + } + + public static GroupSysRequestInvitationEvent Result(uint groupUin, string targetUid, string invitorUid) + => new(groupUin, targetUid, invitorUid); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestJoinEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestJoinEvent.cs new file mode 100644 index 0000000..0bb46a1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysRequestJoinEvent.cs @@ -0,0 +1,16 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysRequestJoinEvent : ProtocolEvent +{ + public string TargetUid { get; } + + public uint GroupUin { get; } + + private GroupSysRequestJoinEvent(uint groupUin, string targetUid) : base(0) + { + TargetUid = targetUid; + GroupUin = groupUin; + } + + public static GroupSysRequestJoinEvent Result(uint groupUin, string targetUid) => new(groupUin, targetUid); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysTodoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysTodoEvent.cs new file mode 100644 index 0000000..d92afa9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/GroupSysTodoEvent.cs @@ -0,0 +1,17 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysTodoEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public string OperatorUid { get; } + + private GroupSysTodoEvent(uint groupUin, string operatorUid) : base(0) + { + GroupUin = groupUin; + OperatorUid = operatorUid; + } + + public static GroupSysTodoEvent Result(uint groupUin, string operatorUid) + => new(groupUin, operatorUid); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/SysPinChangedEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/SysPinChangedEvent.cs new file mode 100644 index 0000000..bf3f2ed --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/Notify/SysPinChangedEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class SysPinChangedEvent : ProtocolEvent +{ + public string Uid { get; } + + public uint? GroupUin { get; } + + public bool IsPin { get; } + + private SysPinChangedEvent(string uid, uint? groupUin, bool isPin) : base(0) + { + Uid = uid; + GroupUin = groupUin; + IsPin = isPin; + } + + public static SysPinChangedEvent Result(string uid, uint? groupUin, bool isPin) => new(uid, groupUin, isPin); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/ProtocolEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/ProtocolEvent.cs new file mode 100644 index 0000000..4082e19 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/ProtocolEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Internal.Event; + +internal class ProtocolEvent +{ + public bool WaitResponse { get; } + + public int ResultCode { get; private set; } + + protected ProtocolEvent(bool waitResponse) => WaitResponse = waitResponse; + + protected ProtocolEvent(int resultCode) => ResultCode = resultCode; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/AliveEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/AliveEvent.cs new file mode 100644 index 0000000..665c068 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/AliveEvent.cs @@ -0,0 +1,10 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class AliveEvent : ProtocolEvent +{ + private AliveEvent() : base(false) { } + + private AliveEvent(int resultCode) : base(resultCode) { } + + public static AliveEvent Create() => new(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/CorrectTimeEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/CorrectTimeEvent.cs new file mode 100644 index 0000000..32d3ed9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/CorrectTimeEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class CorrectTimeEvent : ProtocolEvent +{ + private CorrectTimeEvent() : base(false) { } + + private CorrectTimeEvent(int resultCode) : base(resultCode) { } + + public static CorrectTimeEvent Create() => new(); + + public static CorrectTimeEvent Result() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchClientKeyEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchClientKeyEvent.cs new file mode 100644 index 0000000..b6c30b8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchClientKeyEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchClientKeyEvent : ProtocolEvent +{ + public string ClientKey { get; } + + private FetchClientKeyEvent() : base(true) + { + ClientKey = ""; + } + + private FetchClientKeyEvent(int resultCode, string clientKey) : base(resultCode) + { + ClientKey = clientKey; + } + + public static FetchClientKeyEvent Create() => new(); + + public static FetchClientKeyEvent Result(int resultCode, string clientKey) => new(resultCode, clientKey); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchCookieEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchCookieEvent.cs new file mode 100644 index 0000000..6cb1433 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchCookieEvent.cs @@ -0,0 +1,24 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchCookieEvent : ProtocolEvent +{ + public List Domains { get; set; } + + public List Cookies { get; set; } + + private FetchCookieEvent(List domains) : base(true) + { + Domains = domains; + } + + private FetchCookieEvent(int resultCode, List cookies) : base(resultCode) + { + Cookies = cookies; + } + + public static FetchCookieEvent Create(List domains) => new(domains); + + public static FetchCookieEvent Result(int resultCode, List cookies) => new(resultCode, cookies); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsEvent.cs new file mode 100644 index 0000000..841c7c0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsEvent.cs @@ -0,0 +1,29 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchFriendsEvent : ProtocolEvent +{ + public List Friends { get; } = new(); + + public Dictionary FriendGroups { get; } = new(); + + public uint? NextUin { get; } + + private FetchFriendsEvent(uint? nextUin) : base(true) + { + NextUin = nextUin; + } + + private FetchFriendsEvent(int resultCode, List friends, Dictionary friendGroups, uint? nextUin) : base(resultCode) + { + Friends = friends; + FriendGroups = friendGroups; + NextUin = nextUin; + } + + public static FetchFriendsEvent Create(uint? nextUin = null) => new(nextUin); + + public static FetchFriendsEvent Result(int resultCode, List friends, Dictionary friendGroups, uint? nextUin) => + new(resultCode, friends, friendGroups, nextUin); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsRequestsEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsRequestsEvent.cs new file mode 100644 index 0000000..ca83d17 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFriendsRequestsEvent.cs @@ -0,0 +1,21 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchFriendsRequestsEvent : ProtocolEvent +{ + public List Requests { get; } = new(); + + private FetchFriendsRequestsEvent() : base(true) + { + } + + private FetchFriendsRequestsEvent(int resultCode, List requests) : base(resultCode) + { + Requests = requests; + } + + public static FetchFriendsRequestsEvent Create() => new(); + + public static FetchFriendsRequestsEvent Result(int resultCode, List requests) => new(resultCode, requests); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFullSysFacesEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFullSysFacesEvent.cs new file mode 100644 index 0000000..71c6495 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchFullSysFacesEvent.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchFullSysFacesEvent : ProtocolEvent +{ + public List FacePacks { get; set; } + + private FetchFullSysFacesEvent(List facePacks) : base(true) + { + FacePacks = facePacks; + } + + private FetchFullSysFacesEvent(int resultCode, List facePacks) : base(resultCode) + { + FacePacks = facePacks; + } + + public static FetchFullSysFacesEvent Create() => new(new List()); + + public static FetchFullSysFacesEvent Result(int resultCode, List emojiPacks) => new(resultCode, emojiPacks); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupRequestsEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupRequestsEvent.cs new file mode 100644 index 0000000..997e944 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupRequestsEvent.cs @@ -0,0 +1,34 @@ +namespace Lagrange.Core.Internal.Event.System; + +#pragma warning disable CS8618 + +internal class FetchGroupRequestsEvent : ProtocolEvent +{ + public List Events { get; } + + private FetchGroupRequestsEvent() : base(true) { } + + private FetchGroupRequestsEvent(int resultCode, List events) : base(resultCode) + { + Events = events; + } + + public static FetchGroupRequestsEvent Create() => new(); + + public static FetchGroupRequestsEvent Result(int resultCode, List events) => new(resultCode, events); + + public record RawEvent( + uint GroupUin, + string? InvitorMemberUid, + string? InvitorMemberCard, + string TargetMemberUid, + string TargetMemberCard, + string? OperatorUid, + string? OperatorName, + ulong Sequence, + uint State, + uint EventType, + string? Comment, + bool IsFiltered + ); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupsEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupsEvent.cs new file mode 100644 index 0000000..5e0c804 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchGroupsEvent.cs @@ -0,0 +1,21 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +#pragma warning disable CS8618 + +internal class FetchGroupsEvent : ProtocolEvent +{ + public List Groups { get; } + + private FetchGroupsEvent() : base(true) { } + + private FetchGroupsEvent(List groups) : base(0) + { + Groups = groups; + } + + public static FetchGroupsEvent Create() => new(); + + public static FetchGroupsEvent Result(List groups) => new(groups); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchMembersEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchMembersEvent.cs new file mode 100644 index 0000000..53bd89b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchMembersEvent.cs @@ -0,0 +1,29 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchMembersEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public List Members { get; set; } + + public string? Token { get; } + + private FetchMembersEvent(uint groupUin, string? token) : base(true) + { + GroupUin = groupUin; + Members = new List(); + Token = token; + } + + private FetchMembersEvent(List members, string? token) : base(0) + { + Members = members; + Token = token; + } + + public static FetchMembersEvent Create(uint groupCode, string? token = null) => new(groupCode, token); + + public static FetchMembersEvent Result(List members, string? token) => new(members, token); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchRKeyEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchRKeyEvent.cs new file mode 100644 index 0000000..488a1e3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchRKeyEvent.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +namespace Lagrange.Core.Internal.Event.System; + +internal class FetchRKeyEvent : ProtocolEvent +{ + public List RKeys { get; } = new(); + + private FetchRKeyEvent() : base(true) { } + + private FetchRKeyEvent(int resultCode, List rKeys) : base(resultCode) + { + RKeys = rKeys; + } + + public static FetchRKeyEvent Create() => new(); + + public static FetchRKeyEvent Result(int resultCode, List rKeys) => new(resultCode, rKeys); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchUserInfoEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchUserInfoEvent.cs new file mode 100644 index 0000000..4216bf8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/FetchUserInfoEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.System; + +#pragma warning disable CS8618 + +internal class FetchUserInfoEvent : ProtocolEvent +{ + public BotUserInfo UserInfo { get; } + + public uint Uin { get; } + + public string? Uid { get; } + + private FetchUserInfoEvent(uint uin, string? uid) : base(true) + { + Uin = uin; + Uid = uid; + } + + private FetchUserInfoEvent(int resultCode, BotUserInfo userInfo) : base(resultCode) + { + UserInfo = userInfo; + } + + public static FetchUserInfoEvent Create(uint uid) => new(uid, null); + + public static FetchUserInfoEvent Create(string uid) => new(0, uid); + + public static FetchUserInfoEvent Result(int resultCode, BotUserInfo userInfo) => + new(resultCode, userInfo); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/HighwayUrlEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/HighwayUrlEvent.cs new file mode 100644 index 0000000..0d0bcc4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/HighwayUrlEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.System; + +#pragma warning disable CS8618 + +internal class HighwayUrlEvent : ProtocolEvent +{ + public Dictionary> HighwayUrls { get; } + + public byte[] SigSession { get; } + + private HighwayUrlEvent() : base(true) { } + + private HighwayUrlEvent(int resultCode, byte[] sigSession, Dictionary> highwayUrls) : base(resultCode) + { + SigSession = sigSession; + HighwayUrls = highwayUrls; + } + + public static HighwayUrlEvent Create() => new(); + + public static HighwayUrlEvent Result(int resultCode, byte[] sigSession, Dictionary> highwayUrls) => + new(resultCode, sigSession, highwayUrls); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/InfoSyncEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/InfoSyncEvent.cs new file mode 100644 index 0000000..3e13c9d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/InfoSyncEvent.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.System; + +internal class InfoSyncEvent : ProtocolEvent +{ + public string Message { get; set; } + + private InfoSyncEvent() : base(true) { } + + private InfoSyncEvent(string result) : base(0) => Message = result; + + public static InfoSyncEvent Create() => new(); + + public static InfoSyncEvent Result(string result) => new(result); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/KickNTEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/KickNTEvent.cs new file mode 100644 index 0000000..6a053e2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/KickNTEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Internal.Event.System; + +// ReSharper disable once InconsistentNaming + +internal class KickNTEvent : ProtocolEvent +{ + public string Tag { get; set; } + + public string Message { get; set; } + + private KickNTEvent(string tag, string message) : base(0) + { + Tag = tag; + Message = message; + } + + public static KickNTEvent Create(string tag, string message) => new(tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/LoginNotifyEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/LoginNotifyEvent.cs new file mode 100644 index 0000000..b27fac6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/LoginNotifyEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class LoginNotifyEvent : ProtocolEvent +{ + public bool IsLogin { get; } + + public uint AppId { get; } + + public string Tag { get; } + + public string Message { get; } + + private LoginNotifyEvent(bool isLogin, uint appId, string tag, string message) : base(0) + { + IsLogin = isLogin; + AppId = appId; + Tag = tag; + Message = message; + } + + public static LoginNotifyEvent Result(bool isLogin, uint appId, string tag, string message) => + new(isLogin, appId, tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetFriendRequestEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetFriendRequestEvent.cs new file mode 100644 index 0000000..b581aa1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetFriendRequestEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class SetFriendRequestEvent : ProtocolEvent +{ + public string TargetUid { get; } = string.Empty; + + public bool Accept { get; } + + private SetFriendRequestEvent(string targetUid, bool accept) : base(true) + { + TargetUid = targetUid; + Accept = accept; + } + + private SetFriendRequestEvent(int resultCode) : base(resultCode) { } + + public static SetFriendRequestEvent Create(string targetUid, bool accept) => new(targetUid, accept); + + public static SetFriendRequestEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetNeedToConfirmSwitchEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetNeedToConfirmSwitchEvent.cs new file mode 100644 index 0000000..395b68e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SetNeedToConfirmSwitchEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class SetNeedToConfirmSwitchEvent : ProtocolEvent +{ + public bool EnableNoNeed { get; } + + + private SetNeedToConfirmSwitchEvent(bool enableNoNeed) : base(true) + { + EnableNoNeed = enableNoNeed; + } + + private SetNeedToConfirmSwitchEvent(int resultCode) : base(resultCode) { } + + public static SetNeedToConfirmSwitchEvent Create(bool enableNoNeed) => new(enableNoNeed); + + public static SetNeedToConfirmSwitchEvent Result(int resultCode) => new(resultCode); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/SsoAliveEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SsoAliveEvent.cs new file mode 100644 index 0000000..2432ba3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/SsoAliveEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Internal.Event.System; + +internal class SsoAliveEvent : ProtocolEvent +{ + private SsoAliveEvent() : base(true) { } + + private SsoAliveEvent(int resultCode) : base(resultCode) { } + + public static SsoAliveEvent Create() => new(); + + public static SsoAliveEvent Result() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Event/System/StatusRegisterEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Event/System/StatusRegisterEvent.cs new file mode 100644 index 0000000..8d4dbfc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Event/System/StatusRegisterEvent.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Event.System; + +internal class StatusRegisterEvent : ProtocolEvent +{ + public string Message { get; set; } + + private StatusRegisterEvent() : base(true) { } + + private StatusRegisterEvent(string result) : base(0) => Message = result; + + public static StatusRegisterEvent Create() => new(); + + public static StatusRegisterEvent Result(string result) => new(result); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Network/CallbackClientListener.cs b/Lagrange.Core/Lagrange.Core/Internal/Network/CallbackClientListener.cs new file mode 100644 index 0000000..dfe3fe7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Network/CallbackClientListener.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Internal.Network; + +internal sealed class CallbackClientListener : ClientListener +{ + public override uint HeaderSize => _listener.HeaderSize; + + private readonly IClientListener _listener; + + public CallbackClientListener(IClientListener listener) => _listener = listener; + + public override uint GetPacketLength(ReadOnlySpan header) => _listener.GetPacketLength(header); + + public override void OnDisconnect() => _listener.OnDisconnect(); + + public override void OnRecvPacket(ReadOnlySpan packet) => _listener.OnRecvPacket(packet); + + public override void OnSocketError(Exception e, ReadOnlyMemory data = default) => _listener.OnSocketError(e, data); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.SocketSession.cs b/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.SocketSession.cs new file mode 100644 index 0000000..ad54785 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.SocketSession.cs @@ -0,0 +1,32 @@ +using System.Net.Sockets; + +namespace Lagrange.Core.Internal.Network; + +internal abstract partial class ClientListener +{ + protected sealed class SocketSession : IDisposable + { + public Socket Socket { get; } + + private CancellationTokenSource? _cts; + + public CancellationToken Token { get; } + + public SocketSession() + { + Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _cts = new CancellationTokenSource(); + Token = _cts.Token; + } + + public void Dispose() + { + var cts = Interlocked.Exchange(ref _cts, null); + if (cts == null) return; + + cts.Cancel(); + cts.Dispose(); + Socket.Dispose(); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.cs b/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.cs new file mode 100644 index 0000000..9682b77 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Network/ClientListener.cs @@ -0,0 +1,184 @@ +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Network; + +internal abstract partial class ClientListener : IClientListener +{ + /// + /// Socket connected + /// + public bool Connected => Session?.Socket.Connected ?? false; + + public abstract uint HeaderSize { get; } + + protected SocketSession? Session; + + /// + /// Construct a tcp client + /// + public ClientListener() { } + + private async Task InternalConnectAsync(SocketSession session, string host, int port) + { + try + { + await session.Socket.ConnectAsync(host, port); + _ = ReceiveLoop(session); + return true; + } + catch (Exception e) + { + RemoveSession(session); + OnSocketError(e); + return false; + } + } + + /// + /// Connect to the server + /// + /// + /// + /// + public Task Connect(string host, int port) + { + SocketSession? previousSession = Session, createdSession = null; + if (previousSession != null || // The client has been connected + Interlocked.CompareExchange(ref Session, createdSession = new SocketSession(), null) != null) // Another connect request before this request + { + createdSession?.Dispose(); + return Task.FromResult(false); + } + + return InternalConnectAsync(createdSession, host, port); // Connect to server + } + + /// + /// Disconnect + /// + /// + public void Disconnect() + { + if (Session is { } session) RemoveSession(session); + } + + /// + /// Send data + /// + /// + /// + /// + public async Task Send(ReadOnlyMemory buffer, int timeout = -1) + { + try + { + var session = Session; // Send the data + if (session == null) return false; + + CancellationTokenSource? userCts; + CancellationTokenSource? linkedCts; + CancellationToken token; + if (timeout == -1) + { + userCts = null; + linkedCts = null; + token = session.Token; + } + else + { + userCts = new CancellationTokenSource(timeout); + linkedCts = CancellationTokenSource.CreateLinkedTokenSource(session.Token, userCts.Token); + token = linkedCts.Token; + } + + try + { + return await session.Socket.SendAsync(buffer, SocketFlags.None, token) == buffer.Length; + } + finally + { + userCts?.Dispose(); + linkedCts?.Dispose(); + } + } + catch (Exception e) + { + OnSocketError(e, buffer); + return false; + } + } + + /// + /// Receive the data + /// + private async Task ReceiveLoop(SocketSession session, CancellationToken token = default) + { + try + { + await Task.CompletedTask.ForceAsync(); + Socket socket = session.Socket; + byte[] buffer = new byte[Math.Max(HeaderSize, 2048)]; + int headerSize = (int)HeaderSize; + while (true) + { + await socket.ReceiveFullyAsync(buffer.AsMemory(0, headerSize), token); + int packetLength = (int)GetPacketLength(buffer.AsSpan(0, headerSize)); + if (packetLength > 1024 * 1024 * 64) // limit to 64MiB + { + throw new InvalidDataException($"PackageLength was too long({packetLength})."); + } + if (packetLength > buffer.Length) + { + byte[] newBuffer = new byte[packetLength]; + Unsafe.CopyBlock(ref newBuffer[0], ref buffer[0], (uint)headerSize); + buffer = newBuffer; + } + await socket.ReceiveFullyAsync(buffer.AsMemory(headerSize, packetLength - headerSize), token); + + try + { + OnRecvPacket(buffer.AsSpan(0, packetLength)); + } + catch (Exception e) + { + OnSocketError(e, buffer.AsMemory(0, packetLength)); + throw; + } + } + } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.OperationAborted) + { + + } + catch (Exception e) + { + OnSocketError(e); + } + finally + { + RemoveSession(session); + } + } + + private void RemoveSession(SocketSession session) + { + if (Interlocked.CompareExchange(ref Session, null, session) == session) + { + session.Dispose(); + } + } + + public abstract uint GetPacketLength(ReadOnlySpan header); + + public abstract void OnRecvPacket(ReadOnlySpan packet); + + public abstract void OnDisconnect(); + + public abstract void OnSocketError(Exception e, ReadOnlyMemory data = default); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Network/IClientListener.cs b/Lagrange.Core/Lagrange.Core/Internal/Network/IClientListener.cs new file mode 100644 index 0000000..351d774 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Network/IClientListener.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Internal.Network; + +internal interface IClientListener +{ + uint HeaderSize { get; } + + /// + /// Dissect a stream + /// + /// + public uint GetPacketLength(ReadOnlySpan header); + + /// + /// On handle a packet + /// + public void OnRecvPacket(ReadOnlySpan packet); + + /// + /// On client disconnect + /// + public void OnDisconnect(); + + /// + /// On socket error + /// + public void OnSocketError(Exception e, ReadOnlyMemory data); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamRequest.cs new file mode 100644 index 0000000..c3bd2dd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamRequest.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +[ProtoContract] +internal class FaceRoamRequest +{ + [ProtoMember(1)] public PlatInfo? Comm { get; set; } + + [ProtoMember(2)] public uint SelfUin { get; set; } + + [ProtoMember(3)] public uint SubCmd { get; set; } // 1 + + [ProtoMember(6)] public uint Field6 { get; set; } // 1 +} + +[ProtoContract] +internal class PlatInfo +{ + [ProtoMember(1)] public uint ImPlat { get; set; } + + [ProtoMember(2)] public string? OsVersion { get; set; } + + [ProtoMember(3)] public string? QVersion { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamResponse.cs new file mode 100644 index 0000000..90d0b1e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/FaceRoamResponse.cs @@ -0,0 +1,31 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class FaceRoamResponse +{ + [ProtoMember(1)] public uint RetCode { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } + + [ProtoMember(3)] public uint SubCmd { get; set; } + + [ProtoMember(4)] public FaceRoamUserInfo UserInfo { get; set; } +} + +[ProtoContract] +internal class FaceRoamUserInfo +{ + [ProtoMember(1)] public List FileName { get; set; } + + [ProtoMember(2)] public List DeleteFile { get; set; } + + [ProtoMember(3)] public string Bid { get; set; } + + [ProtoMember(4)] public uint MaxRoamSize { get; set; } + + [ProtoMember(5)] public List EmojiType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn.cs new file mode 100644 index 0000000..a538773 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn.cs @@ -0,0 +1,30 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +[ProtoContract] +internal class HttpConn +{ + [ProtoMember(1)] public int Field1 { get; set; } // 0 + + [ProtoMember(2)] public int Field2 { get; set; } // 0 + + [ProtoMember(3)] public int Field3 { get; set; } // 16 + + [ProtoMember(4)] public int Field4 { get; set; } // 1 + + [ProtoMember(5)] public string Tgt { get; set; } // tgt lower hex + + [ProtoMember(6)] public int Field6 { get; set; } // 3 + + [ProtoMember(7)] public List ServiceTypes { get; set; } // 1, 5, 10, 21 for image + + [ProtoMember(9)] public int Field9 { get; set; } // 2 + + [ProtoMember(10)] public int Field10 { get; set; } // 9 + + [ProtoMember(11)] public int Field11 { get; set; } // 8 + + [ProtoMember(15)] public string Ver { get; set; } // 1.0.1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501.cs new file mode 100644 index 0000000..533eb82 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +[ProtoContract] +internal class HttpConn0x6ff_501 +{ + [ProtoMember(0x501)] public HttpConn HttpConn { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501Response.cs new file mode 100644 index 0000000..b3e4a1d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConn0x6ff_501Response.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +[ProtoContract] +internal class HttpConn0x6ff_501Response +{ + [ProtoMember(0x501)] public HttpConnResponse HttpConn { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConnResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConnResponse.cs new file mode 100644 index 0000000..70efc60 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/HttpConnResponse.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class HttpConnResponse +{ + [ProtoMember(1)] public byte[] SigSession { get; set; } + + [ProtoMember(2)] public byte[] SessionKey { get; set; } + + [ProtoMember(3)] public List ServerInfos { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerAddr.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerAddr.cs new file mode 100644 index 0000000..ef14ddd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerAddr.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +[ProtoContract] +internal class ServerAddr +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] public uint Ip { get; set; } + + [ProtoMember(3)] public uint Port { get; set; } + + [ProtoMember(4)] public uint Area { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerInfo.cs new file mode 100644 index 0000000..83f2786 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/HttpConn/ServerInfo.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action.HttpConn; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class ServerInfo +{ + [ProtoMember(1)] public uint ServiceType { get; set; } + + [ProtoMember(2)] public List ServerAddrs { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyRequest.cs new file mode 100644 index 0000000..70e9a4a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyRequest.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class MarketFaceKeyRequest +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(5)] public MarketFaceInfo Info { get; set; } +} + +[ProtoContract] +internal class MarketFaceInfo +{ + [ProtoMember(3)] public List FaceIds { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyResponse.cs new file mode 100644 index 0000000..b8b8cee --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/MarketFaceKeyResponse.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class MarketFaceKeyResponse +{ + [ProtoMember(5)] public MarketFaceKeyInfo Info { get; set; } +} + +[ProtoContract] +internal class MarketFaceKeyInfo +{ + [ProtoMember(1)] public List Keys { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageRequest.cs new file mode 100644 index 0000000..d99136c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageRequest.cs @@ -0,0 +1,35 @@ +using Lagrange.Core.Internal.Packets.Message; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Action; + +[ProtoContract] +internal class SendMessageRequest +{ + [ProtoMember(1)] public int State { get; set; } + + [ProtoMember(2)] public int SizeCache { get; set; } + + [ProtoMember(3)] public byte[] UnknownFields { get; set; } + + [ProtoMember(4)] public RoutingHead RoutingHead { get; set; } + + [ProtoMember(5)] public ContentHead ContentHead { get; set; } + + [ProtoMember(6)] public MessageBody MessageBody { get; set; } + + [ProtoMember(7)] public int MsgSeq { get; set; } + + [ProtoMember(8)] public int MsgRand { get; set; } + + [ProtoMember(9)] public byte[] SyncCookie { get; set; } + + [ProtoMember(10)] public int MsgVia { get; set; } + + [ProtoMember(11)] public int DataStatist { get; set; } + + [ProtoMember(12)] public MessageControl MessageControl { get; set; } + + [ProtoMember(13)] public int MultiSendSeq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageResponse.cs new file mode 100644 index 0000000..91cba47 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SendMessageResponse.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +[ProtoContract] +internal class SendMessageResponse +{ + [ProtoMember(1)] public int Result { get; set; } + + [ProtoMember(2)] public string? ErrMsg { get; set; } + + [ProtoMember(3)] public uint Timestamp1 { get; set; } + + [ProtoMember(10)] public uint Field10 { get; set; } + + [ProtoMember(11)] public uint? GroupSequence { get; set; } + + [ProtoMember(12)] public uint Timestamp2 { get; set; } + + [ProtoMember(14)] public uint PrivateSequence { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SetStatus.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SetStatus.cs new file mode 100644 index 0000000..66512d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Action/SetStatus.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Action; + +/// +/// trpc.qq_new_tech.status_svc.StatusService.SetStatus +/// +[ProtoContract] +internal class SetStatus +{ + [ProtoMember(1)] public uint Field1 { get; set; } // 10 + + [ProtoMember(2)] public uint Status { get; set; } + + [ProtoMember(3)] public uint ExtStatus { get; set; } + + [ProtoMember(4)] public SetStatusCustomExt? CustomExt { get; set; } +} + +[ProtoContract] +internal class SetStatusCustomExt +{ + [ProtoMember(1)] public uint FaceId { get; set; } + + [ProtoMember(2)] public string? Text { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } // 1 +} + +[ProtoContract] +internal class SetStatusResponse +{ + [ProtoMember(2)] public string Message { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs new file mode 100644 index 0000000..cf407ea --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs @@ -0,0 +1,14 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.Ecdh.Plain; + +[ProtoContract] +internal class SsoKeyExchangeDecrypted +{ + [ProtoMember(1)] public byte[] GcmKey { get; set; } + + [ProtoMember(2)] public byte[] Sign { get; set; } + + [ProtoMember(3)] public uint Expiration { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs new file mode 100644 index 0000000..087f932 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.Ecdh.Plain; + +[ProtoContract] +internal class SsoKeyExchangePlain +{ + [ProtoMember(1)] public string? Uin { get; set; } + + [ProtoMember(2)] public byte[]? Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs new file mode 100644 index 0000000..bf6ccc3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs @@ -0,0 +1,21 @@ +using Lagrange.Core.Utility.Binary; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.Ecdh.Plain; + +/// +/// Sha256 Encrypt this packet to get content of GcmCalc2, Key is constant +/// +[Serializable] +internal class SsoKeyExchangePlain2 +{ + [BinaryProperty(Prefix.None)] public byte[] PublicKey { get; set; } + + [BinaryProperty] public uint Type { get; set; } + + [BinaryProperty(Prefix.None)] public byte[] EncryptedGcm { get; set; } + + [BinaryProperty] public uint Const { get; set; } = 0; + + [BinaryProperty] public uint Timestamp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchange.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchange.cs new file mode 100644 index 0000000..899e6ff --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchange.cs @@ -0,0 +1,21 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.Ecdh; + +/// +/// Request for trpc.login.ecdh.EcdhService.SsoKeyExchange +/// +[ProtoContract] +internal class SsoKeyExchange +{ + [ProtoMember(1)] public byte[] PubKey { get; set; } + + [ProtoMember(2)] public int Type { get; set; } + + [ProtoMember(3)] public byte[] GcmCalc1 { get; set; } + + [ProtoMember(4)] public uint Timestamp { get; set; } + + [ProtoMember(5)] public byte[] GcmCalc2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs new file mode 100644 index 0000000..6f2b5be --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs @@ -0,0 +1,17 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.Ecdh; + +/// +/// Response for trpc.login.ecdh.EcdhService.SsoKeyExchange +/// +[ProtoContract] +internal class SsoKeyExchangeResponse +{ + [ProtoMember(1)] public byte[] GcmEncrypted { get; set; } + + [ProtoMember(2)] public byte[] Body { get; set; } // 腾讯你个傻逼这什么东西啊怎么还带Tag是0的Member + + [ProtoMember(3)] public byte[] PublicKey { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpRequest.cs new file mode 100644 index 0000000..545fc2c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpRequest.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +internal class NTLoginHttpRequest +{ + [JsonPropertyName("appid")] public long Appid { get; set; } + + [JsonPropertyName("faceUpdateTime")] public long FaceUpdateTime { get; set; } + + [JsonPropertyName("qrsig")] public string Qrsig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpResponse.cs new file mode 100644 index 0000000..1249f18 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTLoginHttpResponse.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +internal class NTLoginHttpResponse +{ + [JsonPropertyName("retCode")] public int RetCode { get; set; } + + [JsonPropertyName("errMsg")] public string ErrMsg { get; set; } + + [JsonPropertyName("qrSig")] public string QrSig { get; set; } + + [JsonPropertyName("uin")] public uint Uin { get; set; } + + [JsonPropertyName("faceUrl")] public string FaceUrl { get; set; } + + [JsonPropertyName("faceUpdateTime")] public long FaceUpdateTime { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeQuery.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeQuery.cs new file mode 100644 index 0000000..24a7212 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeQuery.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[Serializable] +internal class NTNewDeviceQrCodeQuery +{ + [JsonPropertyName("uint32_flag")] public long Uint32Flag { get; set; } + + [JsonPropertyName("bytes_token")] public string Token { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeRequest.cs new file mode 100644 index 0000000..fb09619 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrCodeRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[Serializable] +internal class NTNewDeviceQrCodeRequest +{ + [JsonPropertyName("str_dev_auth_token")] public string StrDevAuthToken { get; set; } + + [JsonPropertyName("uint32_flag")] public long Uint32Flag { get; set; } + + [JsonPropertyName("uint32_url_type")] public long Uint32UrlType { get; set; } + + [JsonPropertyName("str_uin_token")] public string StrUinToken { get; set; } + + [JsonPropertyName("str_dev_type")] public string StrDevType { get; set; } + + [JsonPropertyName("str_dev_name")] public string StrDevName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrResponse.cs new file mode 100644 index 0000000..b93e91a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/NTNewDeviceQrResponse.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +#pragma warning disable CS8618 + +[Serializable] +public class NTNewDeviceQrCodeResponse +{ + [JsonPropertyName("uint32_guarantee_status")] public long Uint32GuaranteeStatus { get; set; } + + [JsonPropertyName("str_url")] public string StrUrl { get; set; } + + [JsonPropertyName("ActionStatus")] public string ActionStatus { get; set; } + + [JsonPropertyName("str_nt_succ_token")] public string? StrNtSuccToken { get; set; } + + [JsonPropertyName("ErrorCode")] public long ErrorCode { get; set; } + + [JsonPropertyName("ErrorInfo")] public string ErrorInfo { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginCommon.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginCommon.cs new file mode 100644 index 0000000..ca9025d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginCommon.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; + +// ReSharper disable InconsistentNaming + +internal static class SsoNTLoginCommon +{ + public static Span BuildNTLoginPacket(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, byte[] body) + { + if (keystore.Session.ExchangeKey == null || keystore.Session.KeySign == null) throw new InvalidOperationException("Key is null"); + + var packet = new SsoNTLoginBase + { + Header = new SsoNTLoginHeader + { + Uin = new SsoNTLoginUin { Uin = keystore.Uin.ToString() }, + System = new SsoNTLoginSystem + { + Os = appInfo.Os, + DeviceName = device.DeviceName, + Type = appInfo.NTLoginType, + Guid = device.Guid.ToByteArray() + }, + Version = new SsoNTLoginVersion + { + KernelVersion = device.KernelVersion, + AppId = appInfo.AppId, + PackageName = appInfo.PackageName + }, + Cookie = new SsoNTLoginCookie { Cookie = keystore.Session.UnusualCookies } + }, + Body = new SsoNTLoginEasyLogin { TempPassword = body, } + }; + + if (keystore.Session.Captcha is not null) + { + var (ticket, randStr, aid) = keystore.Session.Captcha.Value; + packet.Body.Captcha = new SsoNTLoginCaptchaSubmit + { + Ticket = ticket, + RandStr = randStr, + Aid = aid + }; + } + + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + var encrypted = AesGcmImpl.Encrypt(stream.ToArray(), keystore.Session.ExchangeKey); + + var encryptPacket = new SsoNTLoginEncryptedData + { + Sign = keystore.Session.KeySign, + GcmCalc = encrypted, + Type = 1 + }; + + return encryptPacket.Serialize(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs new file mode 100644 index 0000000..25e954d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; + +// ReSharper disable once InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginEasyLogin +{ + [ProtoMember(1)] public byte[]? TempPassword { get; set; } + + [ProtoMember(2)] public SsoNTLoginCaptchaSubmit? Captcha { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs new file mode 100644 index 0000000..43f8def --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; + +#pragma warning disable CS8618 +// ReSharper disable once InconsistentNaming + +/// +/// Should be serialized with and encrypted with Tea, key to be investigated +/// +[Serializable] +internal class SsoNTLoginPasswordLogin +{ + [BinaryProperty] public ushort Const4 { get; set; } = 4; + + [BinaryProperty] public uint Random { get; set; } + + [BinaryProperty] public uint UnknownConst2 { get; set; } = 0; + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty] public uint Ver { get; set; } = 8001; + + [BinaryProperty] public uint UnknownConst3 { get; set; } = 0; + + [BinaryProperty] public uint Uin { get; set; } + + [BinaryProperty] public uint Timestamp { get; set; } + + [BinaryProperty] public uint UnknownConst4 { get; set; } = 0; + + [BinaryProperty] public byte Flag { get; set; } = 1; + + [BinaryProperty(Prefix.None)] public byte[] PasswordMd5 { get; set; } + + [BinaryProperty(Prefix.None)] public byte[] RandomBytes { get; set; } + + [BinaryProperty] public uint UnknownConst5 { get; set; } = 0; + + [BinaryProperty] public byte Flag2 { get; set; } = 1; + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; set; } + + [BinaryProperty] public uint UnknownConst6 { get; set; } = 1; + + [BinaryProperty] public uint UnknownConst7 { get; set; } = 1; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string UinString { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs new file mode 100644 index 0000000..0179079 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; + +// Resharper disable InconsistentNaming + +/// +/// Base for trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin and trpc.login.ecdh.EcdhService.SsoNTLoginEasyLogin +/// Should be encrypted and put into +/// +/// Body Type +[ProtoContract] +internal class SsoNTLoginBase where T : class +{ + [ProtoMember(1)] public SsoNTLoginHeader? Header { get; set; } + + [ProtoMember(2)] public T? Body { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs new file mode 100644 index 0000000..d0144b2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs @@ -0,0 +1,20 @@ +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginHeader +{ + [ProtoMember(1)] public SsoNTLoginUin? Uin { get; set; } + + [ProtoMember(2)] public SsoNTLoginSystem? System { get; set; } + + [ProtoMember(3)] public SsoNTLoginVersion? Version { get; set; } + + [ProtoMember(4)] public SsoNTLoginError? Error { get; set; } + + [ProtoMember(5)] public SsoNTLoginCookie? Cookie { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs new file mode 100644 index 0000000..b793d4a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs @@ -0,0 +1,21 @@ +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; + +// ReSharper disable once InconsistentNaming + +/// +/// Response for trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin +/// +[ProtoContract] +internal class SsoNTLoginResponse +{ + [ProtoMember(1)] public SsoNTLoginCredentials? Credentials { get; set; } + + [ProtoMember(2)] public SsoNTLoginCaptchaUrl? Captcha { get; set; } + + [ProtoMember(3)] public SsoNTLoginUnusual? Unusual { get; set; } + + [ProtoMember(4)] public SsoNTLoginUid? Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaSubmit.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaSubmit.cs new file mode 100644 index 0000000..b7da5cf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaSubmit.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginCaptchaSubmit +{ + [ProtoMember(1)] public string Ticket { get; set; } + + [ProtoMember(2)] public string RandStr { get; set; } + + [ProtoMember(3)] public string Aid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaUrl.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaUrl.cs new file mode 100644 index 0000000..7421d1a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCaptchaUrl.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginCaptchaUrl +{ + [ProtoMember(3)] public string Url { get; set; } + + [ProtoIgnore] public string Sid => Url.Split("&sid=")[1].Split("&")[0]; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCookie.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCookie.cs new file mode 100644 index 0000000..f6c990f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCookie.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginCookie +{ + [ProtoMember(1)] public string? Cookie { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs new file mode 100644 index 0000000..a34df19 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginCredentials +{ + [ProtoMember(3)] public byte[] TempPassword { get; set; } + + [ProtoMember(4)] public byte[] Tgt { get; set; } + + [ProtoMember(5)] public byte[] D2 { get; set; } + + [ProtoMember(6)] public byte[] D2Key { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginError.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginError.cs new file mode 100644 index 0000000..abfbf88 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginError.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginError +{ + [ProtoMember(1)] public uint ErrorCode { get; set; } + + [ProtoMember(2)] public string Tag { get; set; } + + [ProtoMember(3)] public string Message { get; set; } + + [ProtoMember(5)] public string? NewDeviceVerifyUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs new file mode 100644 index 0000000..4d14502 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginSystem +{ + [ProtoMember(1)] public string? Os { get; set; } + + [ProtoMember(2)] public string? DeviceName { get; set; } + + [ProtoMember(3)] public int Type { get; set; } = 7; // ? + + [ProtoMember(4)] public byte[]? Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs new file mode 100644 index 0000000..dbf28d0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginUid +{ + [ProtoMember(2)] public string Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs new file mode 100644 index 0000000..3b717a9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginUin +{ + [ProtoMember(1)] public string? Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs new file mode 100644 index 0000000..2b3390c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginUnusual +{ + [ProtoMember(2)] public byte[]? Sig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs new file mode 100644 index 0000000..0eaaf8f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginVersion +{ + [ProtoMember(1)] public string? KernelVersion { get; set; } + + [ProtoMember(2)] public int AppId { get; set; } + + [ProtoMember(3)] public string? PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs new file mode 100644 index 0000000..fec2981 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Internal.Packets.Login.Ecdh.Plain; +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Login.NTLogin; + +[ProtoContract] +internal class SsoNTLoginEncryptedData +{ + /// From Sign field, just simply store that in keystore + [ProtoMember(1)] public byte[]? Sign { get; set; } + + [ProtoMember(3)] public byte[]? GcmCalc { get; set; } + + [ProtoMember(4)] public int Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/Login.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/Login.cs new file mode 100644 index 0000000..0d64307 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/Login.cs @@ -0,0 +1,61 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.Tlv; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; + +internal class Login : WtLoginBase +{ + private const string PacketCommand = "wtlogin.login"; + + private const ushort WtLoginCommand = 2064; + + private const ushort InternalCommand = 0x09; + + private static readonly ushort[] ConstructTlvs = + { + 0x106, 0x144, 0x116, 0x142, 0x145, 0x018, 0x141, 0x177, 0x191, 0x100, 0x107, 0x318, 0x16A, 0x166, 0x521 + }; + + public Login(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(PacketCommand, WtLoginCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructData() => new BinaryPacket() + .WriteUshort(InternalCommand) + .WritePacket(TlvPacker.Pack(ConstructTlvs)); + + + public static Dictionary Deserialize(BinaryPacket packet, BotKeystore keystore, out State state) + { + packet = DeserializePacket(keystore, packet); + + ushort command = packet.ReadUshort(); + if (command != InternalCommand) throw new Exception("Invalid command"); + + state = (State)packet.ReadByte(); + if (state == State.Success) + { + var tlvs = TlvPacker.ReadTlvCollections(packet); + if (tlvs[0x119] is Tlv119 tlv119) + { + var decrypted = keystore.TeaImpl.Decrypt(tlv119.EncryptedTlv, keystore.Stub.TgtgtKey); + var tlv119Packet = new BinaryPacket(decrypted); + return TlvPacker.ReadTlvCollections(tlv119Packet); + } + } + else + { + return TlvPacker.ReadTlvCollections(packet); + } + + return new Dictionary(); + } + + public enum State : byte + { + Success = 0, + Slider = 2, + SmsRequired = 160, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp.cs new file mode 100644 index 0000000..de3e65f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; + +internal abstract class TransEmp : WtLoginBase +{ + private readonly ushort _qrCodeCommand; + + private const string PacketCommand = "wtlogin.trans_emp"; + private const ushort WtLoginCommand = 2066; + + protected TransEmp(ushort qrCmd, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(PacketCommand, WtLoginCommand, keystore, appInfo, device) + { + _qrCodeCommand = qrCmd; + } + + protected override BinaryPacket ConstructData() + { + var tlv = ConstructTlv(); + + var newPacket = new BinaryPacket() // length of WriteUshort(43 + tlv.Length + 1) refers to this section + .WriteByte(2) + .WriteUshort((ushort)(43 + tlv.Length + 1)) // _head_len = 43 + data.size +1 + .WriteUshort(_qrCodeCommand) + .WriteBytes(new byte[21]) + .WriteByte(0x03) + .WriteShort(0x00) // close + .WriteShort(0x32) // Version Code: 50 + .WriteUint(0) // trans_emp sequence + .WriteUlong(0) // dummy uin + .WritePacket(tlv) + .WriteByte(3); + + var requestBody = new BinaryPacket() + .WriteUint((uint)DateTimeOffset.Now.ToUnixTimeSeconds()) + .WritePacket(newPacket); + + return new BinaryPacket() + .WriteByte(0x00) // encryptMethod == EncryptMethod.EM_ST || encryptMethod == EncryptMethod.EM_ECDH_ST + .WriteUshort((ushort)requestBody.Length) + .WriteUint((uint)AppInfo.AppId) + .WriteUint(0x72) // Role + .WriteBytes(Array.Empty(), Prefix.Uint16 | Prefix.LengthOnly) // uSt + .WriteBytes(Array.Empty(), Prefix.Uint8 | Prefix.LengthOnly) // rollback + .WritePacket(requestBody); + } + + public static BinaryPacket DeserializeBody(BotKeystore keystore, BinaryPacket packet, out ushort command) + { + packet = DeserializePacket(keystore, packet); + + uint packetLength = packet.ReadUint(); + packet.Skip(4); // misc unknown data + command = packet.ReadUshort(); + packet.Skip(40); + uint appId = packet.ReadUint(); + + return packet; + } + + protected abstract BinaryPacket ConstructTlv(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp12.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp12.cs new file mode 100644 index 0000000..abc33d5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp12.cs @@ -0,0 +1,50 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; + +internal class TransEmp12 : TransEmp +{ + private const ushort QrCodeCommand = 0x12; + + public TransEmp12(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(QrCodeCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructTlv() + { + if (Keystore.Session.QrSign != null) + { + return new BinaryPacket() + .WriteUshort(0) + .WriteUint((uint)AppInfo.AppId) + .WriteBytes(Keystore.Session.QrSign, Prefix.Uint16 | Prefix.LengthOnly) + .WriteUlong(0) // uin + .WriteByte(0) // version + .WriteBytes(Array.Empty(), Prefix.Uint16 | Prefix.LengthOnly) + .WriteShort(0); + } + + throw new Exception("QrSign is null"); + } + + public static Dictionary Deserialize(BinaryPacket packet, out State qrState) + { + if ((qrState = (State)packet.ReadByte()) == State.Confirmed) + { + packet.Skip(12); // misc unknown data + return TlvPacker.ReadTlvCollections(packet, true); + } + + return new Dictionary(); + } + + internal enum State : byte + { + Confirmed = 0, + CodeExpired = 17, + WaitingForScan = 48, + WaitingForConfirm = 53, + Canceled = 54, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp31.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp31.cs new file mode 100644 index 0000000..4b915b0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/Entity/TransEmp31.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; + +internal class TransEmp31 : TransEmp +{ + private const ushort QrCodeCommand = 0x31; + + private static readonly ushort[] ConstructTlvs = + { + 0x016, 0x01B, 0x01D, 0x033, 0x035, 0x066, 0x0D1 + }; + + private static readonly ushort[] ConstructTlvsPassword = + { + 0x011, 0x016, 0x01B, 0x01D, 0x033, 0x035, 0x066, 0x0D1 + }; + + public TransEmp31(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(QrCodeCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructTlv() => new BinaryPacket() + .WriteUshort(0) + .WriteUint((uint)AppInfo.AppId) + .WriteUlong(0) // uin + .WriteBytes(Array.Empty()) // TGT + .WriteByte(0) + .WriteBytes(Array.Empty(), Prefix.Uint16 | Prefix.LengthOnly) + .WritePacket(TlvPacker.PackQrCode(Keystore.Session.UnusualSign == null ? ConstructTlvs : ConstructTlvsPassword)); + + public static Dictionary Deserialize(BinaryPacket packet, BotKeystore keystore, out byte[] signature) + { + byte dummy = packet.ReadByte(); + signature = packet.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly).ToArray(); + keystore.Session.QrSign = signature; + + return TlvPacker.ReadTlvCollections(packet, true); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/WtLoginBase.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/WtLoginBase.cs new file mode 100644 index 0000000..83f74fe --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Login/WtLogin/WtLoginBase.cs @@ -0,0 +1,83 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Internal.Packets.Login.WtLogin; + +internal abstract class WtLoginBase +{ + protected readonly string Command; + protected readonly ushort Cmd; + protected readonly BotKeystore Keystore; + protected readonly BotAppInfo AppInfo; + protected readonly BotDeviceInfo Device; + + protected readonly TlvPacker TlvPacker; + + protected WtLoginBase(string command, ushort cmd, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + { + Command = command; + Cmd = cmd; + Keystore = keystore; + AppInfo = appInfo; + Device = device; + TlvPacker = new TlvPacker(appInfo, keystore, device); + } + + public BinaryPacket ConstructPacket() + { + var body = ConstructData(); + var encrypt = Keystore.SecpImpl.Encrypt(body.ToArray()); + + var packet = new BinaryPacket() + .WriteByte(2) // packet start + .Barrier(w => w + .WriteUshort(8001) // ver + .WriteUshort(Cmd) // cmd: wtlogin.trans_emp: 2066, wtlogin.login: 2064 + .WriteUshort(Keystore.Session.Sequence) // unique wtLoginSequence for wtlogin packets only, should be stored in KeyStore + .WriteUint(Keystore.Uin) // uin, 0 for wtlogin.trans_emp + .WriteByte(3) // extVer + .WriteByte(135) // cmdVer + .WriteUint(0) // actually unknown const 0 + .WriteByte(19) // pubId + .WriteUshort(0) // insId + .WriteUshort(AppInfo.AppClientVersion) // cliType + .WriteUint(0) // retryTime + .WritePacket(BuildEncryptHead()) + .WriteBytes(encrypt.AsSpan()) + .WriteByte(3), Prefix.Uint16 | Prefix.WithPrefix, 1); // 0x03 is the packet end + + // for the addition of 1, the packet start should be counted in + + return packet; + } + + protected static BinaryPacket DeserializePacket(BotKeystore keystore, BinaryPacket packet) + { + uint packetLength = packet.ReadUint(); + if (packet.ReadByte() != 0x02) return new BinaryPacket(); // packet header + + ushort internalLength = packet.ReadUshort(); + ushort ver = packet.ReadUshort(); + ushort cmd = packet.ReadUshort(); + ushort sequence = packet.ReadUshort(); + uint uin = packet.ReadUint(); + byte flag = packet.ReadByte(); + ushort retryTime = packet.ReadUshort(); + + var encrypted = packet.ReadBytes((int)(packet.Remaining - 1)); + var decrypted = new BinaryPacket(keystore.SecpImpl.Decrypt(encrypted)); + if (packet.ReadByte() != 0x03) throw new Exception("Packet end not found"); // packet end + + return decrypted; + } + + protected abstract BinaryPacket ConstructData(); + + private BinaryPacket BuildEncryptHead() => new BinaryPacket() + .WriteByte(1) // const + .WriteByte(1) // const + .WriteBytes(Keystore.Stub.RandomKey.AsSpan()) // randKey + .WriteUshort(0x102) // unknown const, 腾讯你妈妈死啦 + .WriteBytes(Keystore.SecpImpl.GetPublicKey(), Prefix.Uint16 | Prefix.LengthOnly); // pubKey +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/LongMsgResult.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/LongMsgResult.cs new file mode 100644 index 0000000..d7aa1e1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/LongMsgResult.cs @@ -0,0 +1,24 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +[ProtoContract] +internal class LongMsgResult +{ + [ProtoMember(2)] public List Action { get; set; } +} + +[ProtoContract] +internal class LongMsgAction +{ + [ProtoMember(1)] public string ActionCommand { get; set; } + + [ProtoMember(2)] public LongMsgContent ActionData { get; set; } +} + +[ProtoContract] +internal class LongMsgContent +{ + [ProtoMember(1)] public List MsgBody { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgReq.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgReq.cs new file mode 100644 index 0000000..d144939 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgReq.cs @@ -0,0 +1,42 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +/// +/// trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg Request +/// +[ProtoContract] +internal class RecvLongMsgReq +{ + [ProtoMember(1)] public RecvLongMsgInfo? Info { get; set; } + + [ProtoMember(15)] public LongMsgSettings? Settings { get; set; } +} + +[ProtoContract] +internal class RecvLongMsgInfo +{ + [ProtoMember(1)] public LongMsgUid? Uid { get; set; } + + [ProtoMember(2)] public string? ResId { get; set; } + + [ProtoMember(3)] public bool Acquire { get; set; } // true +} + +[ProtoContract] +internal class LongMsgUid +{ + [ProtoMember(2)] public string? Uid { get; set; } +} + +[ProtoContract] +internal class LongMsgSettings +{ + [ProtoMember(1)] public uint Field1 { get; set; } // 4 + + [ProtoMember(2)] public uint Field2 { get; set; } // 1 + + [ProtoMember(3)] public uint Field3 { get; set; } // 7 + + [ProtoMember(4)] public uint? Field4 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgResp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgResp.cs new file mode 100644 index 0000000..7eb76ed --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/RecvLongMsgResp.cs @@ -0,0 +1,20 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +[ProtoContract] +internal class RecvLongMsgResp +{ + [ProtoMember(1)] public RecvLongMsgResult Result { get; set; } + + [ProtoMember(15)] public LongMsgSettings Settings { get; set; } +} + +[ProtoContract] +internal class RecvLongMsgResult +{ + [ProtoMember(3)] public string ResId { get; set; } + + [ProtoMember(4)] public byte[] Payload { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgReq.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgReq.cs new file mode 100644 index 0000000..ce6eb55 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgReq.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +[ProtoContract] +internal class SendLongMsgReq +{ + [ProtoMember(2)] public SendLongMsgInfo? Info { get; set; } + + [ProtoMember(15)] public LongMsgSettings? Settings { get; set; } +} + +[ProtoContract] +internal class SendLongMsgInfo +{ + [ProtoMember(1)] public uint Type { get; set; } // Group: 3, Friend: 1 + + [ProtoMember(2)] public LongMsgUid? Uid { get; set; } + + [ProtoMember(3)] public uint? GroupUin { get; set; } + + [ProtoMember(4)] public byte[]? Payload { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgResp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgResp.cs new file mode 100644 index 0000000..defe5f2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SendLongMsgResp.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class SendLongMsgResp +{ + [ProtoMember(2)] public SendLongMsgResult Result { get; set; } + + [ProtoMember(15)] public LongMsgSettings Settings { get; set; } +} + +[ProtoContract] +internal class SendLongMsgResult +{ + [ProtoMember(3)] public string ResId { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsg.cs new file mode 100644 index 0000000..63aedf6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsg.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +/// +/// trpc.msg.register_proxy.RegisterProxy.SsoGetC2cMsg +/// +[ProtoContract] +internal class SsoGetC2cMsg +{ + [ProtoMember(2)] public string? FriendUid { get; set; } + + [ProtoMember(3)] public uint StartSequence { get; set; } + + [ProtoMember(4)] public uint EndSequence { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsgResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsgResponse.cs new file mode 100644 index 0000000..b8d0f45 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetC2cMsgResponse.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoGetC2cMsgResponse +{ + [ProtoMember(4)] public string FriendUid { get; set; } + + [ProtoMember(7)] public List? Messages { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsg.cs new file mode 100644 index 0000000..13671d4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsg.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +#pragma warning disable CS8618 + +/// +/// trpc.msg.register_proxy.RegisterProxy.SsoGetGroupMsg +/// +[ProtoContract] +internal class SsoGetGroupMsg +{ + [ProtoMember(1)] public SsoGetGroupMsgInfo Info { get; set; } + + [ProtoMember(2)] public bool Direction { get; set; } // true +} + +[ProtoContract] +internal class SsoGetGroupMsgInfo +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint StartSequence { get; set; } + + [ProtoMember(3)] public uint EndSequence { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsgResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsgResponse.cs new file mode 100644 index 0000000..fde715e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetGroupMsgResponse.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoGetGroupMsgResponse +{ + [ProtoMember(3)] public SsoGetGroupMsgResponseBody Body { get; set; } +} + +[ProtoContract] +internal class SsoGetGroupMsgResponseBody +{ + [ProtoMember(3)] public uint GroupUin { get; set; } + + [ProtoMember(4)] public uint StartSequence { get; set; } + + [ProtoMember(5)] public uint EndSequence { get; set; } + + [ProtoMember(6)] public List Messages { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsg.cs new file mode 100644 index 0000000..6a56376 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsg.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +/// +/// trpc.msg.register_proxy.RegisterProxy.SsoGetRoamMsg +/// +[ProtoContract] +internal class SsoGetRoamMsg +{ + [ProtoMember(1)] public string? FriendUid { get; set; } + + [ProtoMember(2)] public uint Time { get; set; } + + [ProtoMember(3)] public uint Random { get; set; } // 0 + + [ProtoMember(4)] public uint Count { get; set; } + + [ProtoMember(5)] public bool Direction { get; set; } // true +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsgResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsgResponse.cs new file mode 100644 index 0000000..46c9718 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Action/SsoGetRoamMsgResponse.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Action; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoGetRoamMsgResponse +{ + [ProtoMember(3)] public string FriendUid { get; set; } + + [ProtoMember(5)] public uint Timestamp { get; set; } + + [ProtoMember(6)] public uint Random { get; set; } + + [ProtoMember(7)] public List Messages { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2C/C2C.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2C/C2C.cs new file mode 100644 index 0000000..5b5b152 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2C/C2C.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.C2C; + +[ProtoContract] +internal class C2C +{ + [ProtoMember(1)] public uint? Uin { get; set; } + + [ProtoMember(2)] public string? Uid { get; set; } + + [ProtoMember(3)] public uint? Field3 { get; set; } + + [ProtoMember(4)] public uint? Sig { get; set; } + + [ProtoMember(5)] public uint? ReceiverUin { get; set; } + + [ProtoMember(6)] public string? ReceiverUid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2CRecallMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2CRecallMsg.cs new file mode 100644 index 0000000..a51c5cd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/C2CRecallMsg.cs @@ -0,0 +1,42 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class C2CRecallMsg +{ + [ProtoMember(1)] public uint Type { get; set; } // 1 + + [ProtoMember(3)] public string TargetUid { get; set; } // PeerUid in the binary + + [ProtoMember(4)] public C2CRecallMsgInfo Info { get; set; } + + [ProtoMember(5)] public C2CRecallMsgSettings Settings { get; set; } + + [ProtoMember(6)] public bool Field6 { get; set; } // 1 +} + +[ProtoContract] +internal class C2CRecallMsgInfo +{ + [ProtoMember(1)] public uint ClientSequence { get; set; } + + [ProtoMember(2)] public uint Random { get; set; } + + [ProtoMember(3)] public ulong MessageId { get; set; } // 0x01000000 << 32 | Random + + [ProtoMember(4)] public uint Timestamp { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } // 0 + + [ProtoMember(6)] public uint MessageSequence { get; set; } // 700 +} + +[ProtoContract] +internal class C2CRecallMsgSettings +{ + [ProtoMember(1)] public bool Field1 { get; set; } // 0 + + [ProtoMember(2)] public bool Field2 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Attr.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Attr.cs new file mode 100644 index 0000000..d6c8ac6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Attr.cs @@ -0,0 +1,28 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class Attr +{ + [ProtoMember(1)] public int CodePage { get; set; } + + [ProtoMember(2)] public int Time { get; set; } + + [ProtoMember(3)] public int Random { get; set; } + + [ProtoMember(4)] public int Color { get; set; } + + [ProtoMember(5)] public int Size { get; set; } + + [ProtoMember(6)] public int Effect { get; set; } + + [ProtoMember(7)] public int CharSet { get; set; } + + [ProtoMember(8)] public int PitchAndFamily { get; set; } + + [ProtoMember(9)] public string FontName { get; set; } + + [ProtoMember(10)] public byte[] ReserveData { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ButtonExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ButtonExtra.cs new file mode 100644 index 0000000..42cb041 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ButtonExtra.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Message.Entity; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class ButtonExtra +{ + [ProtoMember(1)] public KeyboardData Data { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/FileExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/FileExtra.cs new file mode 100644 index 0000000..662e5dc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/FileExtra.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +[ProtoContract] +internal class FileExtra +{ + [ProtoMember(1)] public NotOnlineFile? File { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GreyTipExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GreyTipExtra.cs new file mode 100644 index 0000000..8985e4a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GreyTipExtra.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +[ProtoContract] +internal class GreyTipExtra +{ + [ProtoMember(101)] public GreyTipExtraLayer1? Layer1 { get; set; } +} + +[ProtoContract] +internal class GreyTipExtraLayer1 +{ + [ProtoMember(1)] public GreyTipExtraInfo? Info { get; set; } +} + +[ProtoContract] +internal class GreyTipExtraInfo +{ + [ProtoMember(1)] public uint Type { get; set; } // 1 + + [ProtoMember(2)] public string Content { get; set; } = string.Empty; // "{\"gray_tip\":\"谨防兼职刷单、游戏交易、投资荐股、色情招嫖、仿冒公检法及客服人员的网络骗局,如有资金往来请谨慎操作。 \",\"object_type\":3,\"sub_type\":2,\"type\":4}" +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GroupFileExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GroupFileExtra.cs new file mode 100644 index 0000000..9067e97 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/GroupFileExtra.cs @@ -0,0 +1,41 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupFileExtra +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public string FileName { get; set; } + + [ProtoMember(3)] public string Display { get; set; } + + [ProtoMember(7)] public GroupFileExtraInner Inner { get; set; } +} + +[ProtoContract] +internal class GroupFileExtraInner +{ + [ProtoMember(2)] public GroupFileExtraInfo Info { get; set; } +} + +[ProtoContract] +internal class GroupFileExtraInfo +{ + [ProtoMember(1)] public uint BusId { get; set; } + + [ProtoMember(2)] public string FileId { get; set; } + + [ProtoMember(3)] public long FileSize { get; set; } + + [ProtoMember(4)] public string FileName { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } + + [ProtoMember(7)] public string Field7 { get; set; } + + [ProtoMember(8)] public string FileMd5 { get; set; } // hexed +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ImageExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ImageExtra.cs new file mode 100644 index 0000000..2102bc3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/ImageExtra.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +[ProtoContract] +internal class ImageExtraUrl +{ + [ProtoMember(30)] public string OrigUrl { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/PokeExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/PokeExtra.cs new file mode 100644 index 0000000..988a667 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Extra/PokeExtra.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component.Extra; + +[ProtoContract] +internal class PokeExtra +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(7)] public uint Strength { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/GroupInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/GroupInfo.cs new file mode 100644 index 0000000..f138015 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/GroupInfo.cs @@ -0,0 +1,22 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class GroupInfo +{ + [ProtoMember(1)] public ulong GroupCode { get; set; } + + [ProtoMember(2)] public int GroupType { get; set; } + + [ProtoMember(3)] public long GroupInfoSeq { get; set; } + + [ProtoMember(4)] public string GroupCard { get; set; } + + [ProtoMember(5)] public int GroupLevel { get; set; } + + [ProtoMember(6)] public int GroupCardType { get; set; } + + [ProtoMember(7)] public byte[] GroupName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/MutilTransHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/MutilTransHead.cs new file mode 100644 index 0000000..7d2e48f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/MutilTransHead.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class MutilTransHead +{ + [ProtoMember(1)] public int Status { get; set; } + + [ProtoMember(2)] public int MsgId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/NotOnlineFile.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/NotOnlineFile.cs new file mode 100644 index 0000000..16b0918 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/NotOnlineFile.cs @@ -0,0 +1,48 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class NotOnlineFile +{ + [ProtoMember(1)] public int? FileType { get; set; } + + [ProtoMember(2)] public byte[]? Sig { get; set; } + + [ProtoMember(3)] public string? FileUuid { get; set; } + + [ProtoMember(4)] public byte[]? FileMd5 { get; set; } + + [ProtoMember(5)] public string? FileName { get; set; } + + [ProtoMember(6)] public long? FileSize { get; set; } + + [ProtoMember(7)] public byte[]? Note { get; set; } + + [ProtoMember(8)] public int? Reserved { get; set; } + + [ProtoMember(9)] public int? Subcmd { get; set; } + + [ProtoMember(10)] public int? MicroCloud { get; set; } + + [ProtoMember(11)] public List? BytesFileUrls { get; set; } + + [ProtoMember(12)] public int? DownloadFlag { get; set; } + + [ProtoMember(50)] public int? DangerEvel { get; set; } + + [ProtoMember(51)] public int? LifeTime { get; set; } + + [ProtoMember(52)] public int? UploadTime { get; set; } + + [ProtoMember(53)] public int? AbsFileType { get; set; } + + [ProtoMember(54)] public int? ClientType { get; set; } + + [ProtoMember(55)] public int? ExpireTime { get; set; } + + [ProtoMember(56)] public byte[]? PbReserve { get; set; } + + [ProtoMember(57)] public string? FileHash { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/PcSupportDef.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/PcSupportDef.cs new file mode 100644 index 0000000..49e6f59 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/PcSupportDef.cs @@ -0,0 +1,20 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class PcSupportDef +{ + [ProtoMember(1)] public uint PcPtlBegin { get; set; } + + [ProtoMember(2)] public uint PcPtlEnd { get; set; } + + [ProtoMember(3)] public uint MacPtlBegin { get; set; } + + [ProtoMember(4)] public uint MacPtlEnd { get; set; } + + [ProtoMember(5)] public List PtlsSupport { get; set; } + + [ProtoMember(6)] public List PtlsNotSupport { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Ptt.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Ptt.cs new file mode 100644 index 0000000..93962fd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/Ptt.cs @@ -0,0 +1,56 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class Ptt +{ + [ProtoMember(1)] public int FileType { get; set; } + + [ProtoMember(2)] public long SrcUin { get; set; } + + [ProtoMember(3)] public string FileUuid { get; set; } + + [ProtoMember(4)] public byte[] FileMd5 { get; set; } + + [ProtoMember(5)] public string FileName { get; set; } + + [ProtoMember(6)] public int FileSize { get; set; } + + [ProtoMember(7)] public byte[] Reserve { get; set; } + + [ProtoMember(8)] public int FileId { get; set; } + + [ProtoMember(9)] public int ServerIp { get; set; } + + [ProtoMember(10)] public int ServerPort { get; set; } + + [ProtoMember(11)] public bool BoolValid { get; set; } + + [ProtoMember(12)] public byte[] Signature { get; set; } + + [ProtoMember(13)] public byte[] Shortcut { get; set; } + + [ProtoMember(14)] public byte[] FileKey { get; set; } + + [ProtoMember(15)] public int MagicPttIndex { get; set; } + + [ProtoMember(16)] public int VoiceSwitch { get; set; } + + [ProtoMember(17)] public byte[] PttUrl { get; set; } + + [ProtoMember(18)] public string GroupFileKey { get; set; } + + [ProtoMember(19)] public int Time { get; set; } + + [ProtoMember(20)] public byte[] DownPara { get; set; } + + [ProtoMember(29)] public int Format { get; set; } + + [ProtoMember(30)] public byte[] PbReserve { get; set; } + + [ProtoMember(31)] public List BytesPttUrls { get; set; } + + [ProtoMember(32)] public int DownloadFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/RichText.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/RichText.cs new file mode 100644 index 0000000..2cac79d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Component/RichText.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Internal.Packets.Message.Element; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Component; + +[ProtoContract] +internal class RichText +{ + [ProtoMember(1)] public Attr? Attr { get; set; } + + [ProtoMember(2)] public List Elems { get; set; } + + [ProtoMember(3)] public NotOnlineFile? NotOnlineFile { get; set; } + + [ProtoMember(4)] public Ptt? Ptt { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ContentHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ContentHead.cs new file mode 100644 index 0000000..8bc2c1b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ContentHead.cs @@ -0,0 +1,35 @@ +using Lagrange.Core.Internal.Packets.Message.Routing; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class ContentHead +{ + [ProtoMember(1)] public uint Type { get; set; } // 消息类型 + + [ProtoMember(2)] public uint? SubType { get; set; } // 消息子类型(0x211\0x2dc\0x210等系统消息的子类型,取值同c2c_cmd) + + [ProtoMember(3)] public uint? C2CCmd { get; set; } // c2c消息子类型 + + [ProtoMember(4)] public long? Random { get; set; } + + [ProtoMember(5)] public uint? Sequence { get; set; } + + [ProtoMember(6)] public long? Timestamp { get; set; } + + [ProtoMember(7)] public long? PkgNum { get; set; } // 分包数目,消息需要分包发送时该值不为1 + + [ProtoMember(8)] public uint? PkgIndex { get; set; } // 当前分包索引,从 0开始 + + [ProtoMember(9)] public uint? DivSeq { get; set; } // 消息分包的序列号,同一条消息的各个分包的 div_seq 相同 + + [ProtoMember(10)] public uint AutoReply { get; set; } + + [ProtoMember(11)] public uint? NTMsgSeq { get; set; } // 两个uin之间c2c消息唯一递增seq + + [ProtoMember(12)] public ulong? MsgUid { get; set; } + + [ProtoMember(15)] public ForwardHead? Forward { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Elem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Elem.cs new file mode 100644 index 0000000..c5d12bf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Elem.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Message.Element; + +[ProtoContract] +internal class Elem +{ + [ProtoMember(1)] public Text? Text { get; set; } // TextEntity + + [ProtoMember(2)] public Face? Face { get; set; } + + [ProtoMember(3)] public OnlineImage? OnlineImage { get; set; } + + [ProtoMember(4)] public NotOnlineImage? NotOnlineImage { get; set; } // Offline Image + + [ProtoMember(5)] public TransElem? TransElem { get; set; } + + [ProtoMember(6)] public Marketface? Marketface { get; set; } + + [ProtoMember(8)] public CustomFace? CustomFace { get; set; } + + [ProtoMember(9)] public ElemFlags2? ElemFlags2 { get; set; } + + [ProtoMember(12)] public RichMsg? RichMsg { get; set; } + + [ProtoMember(13)] public GroupFile? GroupFile { get; set; } + + [ProtoMember(16)] public ExtraInfo? ExtraInfo { get; set; } + + [ProtoMember(19)] public VideoFile? VideoFile { get; set; } + + [ProtoMember(21)] public AnonymousGroupMessage? AnonGroupMsg { get; set; } + + [ProtoMember(24)] public QQWalletMsg? QQWalletMsg { get; set; } + + [ProtoMember(31)] public CustomElem? CustomElem { get; set; } + + [ProtoMember(37)] public GeneralFlags? GeneralFlags { get; set; } + + [ProtoMember(45)] public SrcMsg? SrcMsg { get; set; } // Forward/ReplyEntity + + [ProtoMember(51)] public LightAppElem? LightAppElem { get; set; } + + [ProtoMember(53)] public CommonElem? CommonElem { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs new file mode 100644 index 0000000..4c20153 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs @@ -0,0 +1,22 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class AnonymousGroupMessage +{ + [ProtoMember(1)] public int Flags { get; set; } + + [ProtoMember(2)] public byte[] AnonId { get; set; } + + [ProtoMember(3)] public byte[] AnonNick { get; set; } + + [ProtoMember(4)] public int HeadPortrait { get; set; } + + [ProtoMember(5)] public int ExpireTime { get; set; } + + [ProtoMember(6)] public int BubbleId { get; set; } + + [ProtoMember(7)] public byte[] RankColor { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CommonElem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CommonElem.cs new file mode 100644 index 0000000..366fd12 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CommonElem.cs @@ -0,0 +1,14 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CommonElem +{ + [ProtoMember(1)] public int ServiceType { get; set; } + + [ProtoMember(2)] public byte[] PbElem { get; set; } + + [ProtoMember(3)] public uint BusinessType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomElem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomElem.cs new file mode 100644 index 0000000..2fc845f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomElem.cs @@ -0,0 +1,18 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CustomElem +{ + [ProtoMember(1)] public byte[] Desc { get; set; } + + [ProtoMember(2)] public byte[] Data { get; set; } + + [ProtoMember(3)] public int EnumType { get; set; } + + [ProtoMember(4)] public byte[] Ext { get; set; } + + [ProtoMember(5)] public byte[] Sound { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomFace.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomFace.cs new file mode 100644 index 0000000..7cf56f4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/CustomFace.cs @@ -0,0 +1,112 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CustomFace +{ + [ProtoMember(1)] public byte[] Guid { get; set; } + + [ProtoMember(2)] public string FilePath { get; set; } + + [ProtoMember(3)] public string Shortcut { get; set; } + + [ProtoMember(4)] public byte[] Buffer { get; set; } + + [ProtoMember(5)] public byte[] Flag { get; set; } + + [ProtoMember(6)] public byte[]? OldData { get; set; } + + [ProtoMember(7)] public uint FileId { get; set; } + + [ProtoMember(8)] public int? ServerIp { get; set; } + + [ProtoMember(9)] public int? ServerPort { get; set; } + + [ProtoMember(10)] public int FileType { get; set; } + + [ProtoMember(11)] public byte[] Signature { get; set; } + + [ProtoMember(12)] public int Useful { get; set; } + + [ProtoMember(13)] public byte[] Md5 { get; set; } + + [ProtoMember(14)] public string ThumbUrl { get; set; } + + [ProtoMember(15)] public string BigUrl { get; set; } + + [ProtoMember(16)] public string OrigUrl { get; set; } + + [ProtoMember(17)] public int BizType { get; set; } + + [ProtoMember(18)] public int RepeatIndex { get; set; } + + [ProtoMember(19)] public int RepeatImage { get; set; } + + [ProtoMember(20)] public int ImageType { get; set; } + + [ProtoMember(21)] public int Index { get; set; } + + [ProtoMember(22)] public int Width { get; set; } + + [ProtoMember(23)] public int Height { get; set; } + + [ProtoMember(24)] public int Source { get; set; } + + [ProtoMember(25)] public uint Size { get; set; } + + [ProtoMember(26)] public int Origin { get; set; } + + [ProtoMember(27)] public int? ThumbWidth { get; set; } + + [ProtoMember(28)] public int? ThumbHeight { get; set; } + + [ProtoMember(29)] public int ShowLen { get; set; } + + [ProtoMember(30)] public int DownloadLen { get; set; } + + [ProtoMember(31)] public string? X400Url { get; set; } + + [ProtoMember(32)] public int X400Width { get; set; } + + [ProtoMember(33)] public int X400Height { get; set; } + + [ProtoMember(34)] public PbReserve1? PbReserve { get; set; } + + [ProtoContract] + public class PbReserve1 + { + [ProtoMember(1)] public int SubType { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(9)] public string Summary { get; set; } + + [ProtoMember(10)] public int Field10 { get; set; } + + [ProtoMember(21)] public PbReserve2 Field21 { get; set; } + + [ProtoMember(31)] public string Field31 { get; set; } + } + + [ProtoContract] + public class PbReserve2 + { + [ProtoMember(1)] public int Field1 { get; set; } + + [ProtoMember(2)] public string Field2 { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } + + [ProtoMember(7)] public string Md5Str { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs new file mode 100644 index 0000000..c2b21e3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +internal partial class ElemFlags2 +{ + [ProtoContract] + public class Instance + { + [ProtoMember(1)] public uint AppId { get; set; } + + [ProtoMember(2)] public uint InstId { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.cs new file mode 100644 index 0000000..6eff14a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ElemFlags2.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Internal.Packets.Message.Component; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class ElemFlags2 +{ + [ProtoMember(1)] public uint ColorTextId { get; set; } + + [ProtoMember(2)] public ulong MsgId { get; set; } + + [ProtoMember(3)] public uint WhisperSessionId { get; set; } + + [ProtoMember(4)] public uint PttChangeBit { get; set; } + + [ProtoMember(5)] public uint VipStatus { get; set; } + + [ProtoMember(6)] public uint CompatibleId { get; set; } + + [ProtoMember(7)] public List Insts { get; set; } + + [ProtoMember(8)] public uint MsgRptCnt { get; set; } + + [ProtoMember(9)] public Instance SrcInst { get; set; } + + [ProtoMember(10)] public uint Longtitude { get; set; } + + [ProtoMember(11)] public uint Latitude { get; set; } + + [ProtoMember(12)] public uint CustomFont { get; set; } + + [ProtoMember(13)] public PcSupportDef PcSupportDef { get; set; } + + [ProtoMember(14)] public uint? CrmFlags { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/CustomFaceExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/CustomFaceExtra.cs new file mode 100644 index 0000000..c6fd0c2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/CustomFaceExtra.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class CustomFaceExtra +{ + [ProtoMember(31)] public string? Hash { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FaceExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FaceExtra.cs new file mode 100644 index 0000000..16fa6bb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FaceExtra.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +/// +/// +/// +[ProtoContract] +internal class FaceExtra +{ + [ProtoMember(1)] public int? FaceId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FriendShakeExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FriendShakeExtra.cs new file mode 100644 index 0000000..de45def --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/FriendShakeExtra.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class FriendShakeExtra +{ + [ProtoMember(1)] public uint FaceId { get; set; } + + [ProtoMember(7)] public uint Strength { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/GroupReactionExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/GroupReactionExtra.cs new file mode 100644 index 0000000..7973fff --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/GroupReactionExtra.cs @@ -0,0 +1,41 @@ +#pragma warning disable CS8618 + +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class GroupReactionExtra +{ + [ProtoMember(1)] public GroupReactionExtraBody Body { get; set; } +} + +[ProtoContract] +internal class GroupReactionExtraBody +{ + [ProtoMember(1)] public GroupReactionExtraBodyField1 Field1 { get; set; } + + [ProtoMember(2)] public GroupReactionExtraFaceInfo[] FaceInfos { get; set; } +} + +[ProtoContract] +internal class GroupReactionExtraBodyField1 +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } +} + +[ProtoContract] +internal class GroupReactionExtraFaceInfo +{ + // See Lagrange.Core/Internal/Service/System/FetchFullSysFacesService.cs + [ProtoMember(1)] public string FaceId { get; set; } + + // See Lagrange.Core/Internal/Service/System/FetchFullSysFacesService.cs + [ProtoMember(2)] public uint Type { get; set; } + + [ProtoMember(3)] public uint Count { get; set; } + + [ProtoMember(4)] public uint IsAdded { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/ImageExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/ImageExtra.cs new file mode 100644 index 0000000..fcd2f9d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/ImageExtra.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +/// +/// Used in conjunction with , should be created in a dedicated and put in Leaf 53 +/// NTQQ Exclusive +/// +[ProtoContract] +internal class ImageExtra +{ + [ProtoMember(85)] public uint Field85 { get; set; } // 1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/MentionExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/MentionExtra.cs new file mode 100644 index 0000000..a1d0bf6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/MentionExtra.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +/// +/// , ProtoMember12 (PbReserve) +/// +[ProtoContract] +internal class MentionExtra +{ + [ProtoMember(3)] public int? Type { get; set; } // 1 for All Member, 2 for Specific Member + + [ProtoMember(4)] public uint? Uin { get; set; } + + [ProtoMember(5)] public int? Field5 { get; set; } // Const Zero + + [ProtoMember(9)] public string? Uid { get; set; } // set this string to "all" to mention everyone in the Group +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBigFaceExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBigFaceExtra.cs new file mode 100644 index 0000000..db7a9de --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBigFaceExtra.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +/// +/// Constructed at , Service Type 33, Big face +/// +[ProtoContract] +internal class QBigFaceExtra +{ + [ProtoMember(1)] public string? AniStickerPackId { get; set; } + + [ProtoMember(2)] public string? AniStickerId { get; set; } + + [ProtoMember(3)] public int? FaceId { get; set; } // 318 + + [ProtoMember(4)] public int? Field4 { get; set; } + + [ProtoMember(5)] public int? AniStickerType { get; set; } + + [ProtoMember(6)] public string? Field6 { get; set; } + + [ProtoMember(7)] public string? Preview { get; set; } + + [ProtoMember(9)] public int? Field9 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs new file mode 100644 index 0000000..4c2523f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QBounceFaceExtra.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS8618 + +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class QBounceFaceExtra +{ + [ProtoMember(1)] public int Field1 { get; set; } = 13; + + [ProtoMember(2)] public uint Count { get; set; } = 1; + + [ProtoMember(3)] public string Name { get; set; } = string.Empty; + + [ProtoMember(6)] public QSmallFaceExtra Face { get; set; } + + [ProtoContract] + public class FallbackPreviewTextPb + { + [ProtoMember(1)] public string Text { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QSmallFaceExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QSmallFaceExtra.cs new file mode 100644 index 0000000..749dcd9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/QSmallFaceExtra.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +/// +/// Constructed at , Service Type 33, Small face (FaceId >= 260) +/// +[ProtoContract] +internal class QSmallFaceExtra +{ + [ProtoMember(1)] public uint FaceId { get; set; } + + [ProtoMember(2)] public string? Text { get; set; } + + [ProtoMember(3)] public string? CompatText { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/SpecialPokeExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/SpecialPokeExtra.cs new file mode 100644 index 0000000..c414f52 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Extra/SpecialPokeExtra.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; + +[ProtoContract] +internal class SpecialPokeExtra +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] public uint Count { get; set; } + + [ProtoMember(3)] public string FaceName { get; set; } = string.Empty; + + // [ProtoMember(6)] public byte[] PbReserve { get; set; } = Array.Empty(); +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ExtraInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ExtraInfo.cs new file mode 100644 index 0000000..681788a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/ExtraInfo.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class ExtraInfo +{ + [ProtoMember(1)] public byte[] Nick { get; set; } + + [ProtoMember(2)] public byte[] GroupCard { get; set; } + + [ProtoMember(3)] public int Level { get; set; } + + [ProtoMember(4)] public int Flags { get; set; } + + [ProtoMember(5)] public int GroupMask { get; set; } + + [ProtoMember(6)] public int MsgTailId { get; set; } + + [ProtoMember(7)] public byte[] SenderTitle { get; set; } + + [ProtoMember(8)] public byte[] ApnsTips { get; set; } + + [ProtoMember(9)] public ulong Uin { get; set; } + + [ProtoMember(10)] public int MsgStateFlag { get; set; } + + [ProtoMember(11)] public int ApnsSoundType { get; set; } + + [ProtoMember(12)] public int NewGroupFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Face.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Face.cs new file mode 100644 index 0000000..2c7c232 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Face.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class Face +{ + [ProtoMember(1)] public int? Index { get; set; } + + [ProtoMember(2)] public byte[]? Old { get; set; } + + [ProtoMember(11)] public byte[]? Buf { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GeneralFlags.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GeneralFlags.cs new file mode 100644 index 0000000..331e65c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GeneralFlags.cs @@ -0,0 +1,46 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class GeneralFlags +{ + [ProtoMember(1)] public int BubbleDiyTextId { get; set; } + + [ProtoMember(2)] public int GroupFlagNew { get; set; } + + [ProtoMember(3)] public ulong Uin { get; set; } + + [ProtoMember(4)] public byte[] RpId { get; set; } + + [ProtoMember(5)] public int PrpFold { get; set; } + + [ProtoMember(6)] public int LongTextFlag { get; set; } + + [ProtoMember(7)] public string? LongTextResId { get; set; } + + [ProtoMember(8)] public int GroupType { get; set; } + + [ProtoMember(9)] public int ToUinFlag { get; set; } + + [ProtoMember(10)] public int GlamourLevel { get; set; } + + [ProtoMember(11)] public int MemberLevel { get; set; } + + [ProtoMember(12)] public long GroupRankSeq { get; set; } + + [ProtoMember(13)] public int OlympicTorch { get; set; } + + [ProtoMember(14)] public byte[] BabyqGuideMsgCookie { get; set; } + + [ProtoMember(15)] public int Uin32ExpertFlag { get; set; } + + [ProtoMember(16)] public int BubbleSubId { get; set; } + + [ProtoMember(17)] public long PendantId { get; set; } + + [ProtoMember(18)] public byte[] RpIndex { get; set; } + + [ProtoMember(19)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GroupFile.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GroupFile.cs new file mode 100644 index 0000000..b3256e1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/GroupFile.cs @@ -0,0 +1,30 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class GroupFile +{ + [ProtoMember(1)] public byte[] Filename { get; set; } + + [ProtoMember(2)] public long FileSize { get; set; } + + [ProtoMember(3)] public byte[] FileId { get; set; } + + [ProtoMember(4)] public byte[] BatchId { get; set; } + + [ProtoMember(5)] public byte[] FileKey { get; set; } + + [ProtoMember(6)] public byte[] Mark { get; set; } + + [ProtoMember(7)] public long Sequence { get; set; } + + [ProtoMember(8)] public byte[] BatchItemId { get; set; } + + [ProtoMember(9)] public int FeedMsgTime { get; set; } + + [ProtoMember(10)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/LightAppElem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/LightAppElem.cs new file mode 100644 index 0000000..fce6add --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/LightAppElem.cs @@ -0,0 +1,12 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class LightAppElem +{ + [ProtoMember(1)] public byte[] Data { get; set; } + + [ProtoMember(2)] public byte[]? MsgResid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Marketface.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Marketface.cs new file mode 100644 index 0000000..f801378 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Marketface.cs @@ -0,0 +1,37 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class Marketface +{ + [ProtoMember(1)] public string Summary { get; set; } + + [ProtoMember(2)] public int ItemType { get; set; } + + [ProtoMember(3)] public int Info { get; set; } + + [ProtoMember(4)] public byte[] FaceId { get; set; } + + [ProtoMember(5)] public int TabId { get; set; } + + [ProtoMember(6)] public int SubType { get; set; } + + [ProtoMember(7)] public string Key { get; set; } + + [ProtoMember(10)] public int Width { get; set; } + + [ProtoMember(11)] public int Height { get; set; } + + [ProtoMember(13)] public MarketfaceReserve PbReserve { get; set; } +} + +[ProtoContract] +internal class MarketfaceReserve +{ + [ProtoMember(8)] public int Field8 { get; set; } + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/NotOnlineImage.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/NotOnlineImage.cs new file mode 100644 index 0000000..e7fa1fb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/NotOnlineImage.cs @@ -0,0 +1,104 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class NotOnlineImage +{ + [ProtoMember(1)] public string FilePath { get; set; } + + [ProtoMember(2)] public uint FileLen { get; set; } + + [ProtoMember(3)] public string DownloadPath { get; set; } + + [ProtoMember(4)] public byte[] OldVerSendFile { get; set; } + + [ProtoMember(5)] public int ImgType { get; set; } + + [ProtoMember(6)] public byte[] PreviewsImage { get; set; } + + [ProtoMember(7)] public byte[] PicMd5 { get; set; } + + [ProtoMember(8)] public uint PicHeight { get; set; } + + [ProtoMember(9)] public uint PicWidth { get; set; } + + [ProtoMember(10)] public string ResId { get; set; } + + [ProtoMember(11)] public byte[] Flag { get; set; } + + [ProtoMember(12)] public string ThumbUrl { get; set; } + + [ProtoMember(13)] public int Original { get; set; } + + [ProtoMember(14)] public string BigUrl { get; set; } + + [ProtoMember(15)] public string OrigUrl { get; set; } + + [ProtoMember(16)] public int BizType { get; set; } + + [ProtoMember(17)] public int Result { get; set; } + + [ProtoMember(18)] public int Index { get; set; } + + [ProtoMember(19)] public byte[] OpFaceBuf { get; set; } + + [ProtoMember(20)] public bool OldPicMd5 { get; set; } + + [ProtoMember(21)] public int ThumbWidth { get; set; } + + [ProtoMember(22)] public int ThumbHeight { get; set; } + + [ProtoMember(23)] public int FileId { get; set; } + + [ProtoMember(24)] public uint ShowLen { get; set; } + + [ProtoMember(25)] public uint DownloadLen { get; set; } + + [ProtoMember(26)] public string? X400Url { get; set; } + + [ProtoMember(27)] public uint X400Width { get; set; } + + [ProtoMember(28)] public uint X400Height { get; set; } + + [ProtoMember(29)] public PbReserve PbRes { get; set; } + + [ProtoContract] + public class PbReserve + { + [ProtoMember(1)] public int SubType { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(8)] public string Summary { get; set; } + + [ProtoMember(10)] public int Field10 { get; set; } + + [ProtoMember(20)] public PbReserve2 Field20 { get; set; } + + [ProtoMember(30)] public string Url { get; set; } + + [ProtoMember(31)] public string Md5Str { get; set; } + } + + [ProtoContract] + public class PbReserve2 + { + [ProtoMember(1)] public int Field1 { get; set; } + + [ProtoMember(2)] public string Field2 { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } + + [ProtoMember(7)] public string Field7 { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/OnlineImage.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/OnlineImage.cs new file mode 100644 index 0000000..b04f698 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/OnlineImage.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class OnlineImage +{ + [ProtoMember(1)] public byte[] Guid { get; set; } + + [ProtoMember(2)] public byte[] FilePath { get; set; } + + [ProtoMember(3)] public byte[] OldVerSendFile { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs new file mode 100644 index 0000000..aa4a7a2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs @@ -0,0 +1,55 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +internal partial class QQWalletMsg +{ + [ProtoContract] + internal class QQWalletAioBody + { + [ProtoMember(1)] public ulong SendUin { get; set; } + + [ProtoMember(2)] public QQWalletAioElem Sender { get; set; } + + [ProtoMember(3)] public QQWalletAioElem Receiver { get; set; } + + [ProtoMember(4, DataFormat = DataFormat.ZigZag)] public int ChannelId { get; set; } + + [ProtoMember(5, DataFormat = DataFormat.ZigZag)] public int TemplateId { get; set; } + + [ProtoMember(6)] public uint Resend { get; set; } + + [ProtoMember(7)] public uint MsgPriority { get; set; } + + [ProtoMember(8, DataFormat = DataFormat.ZigZag)] public int RedType { get; set; } + + [ProtoMember(9)] public byte[] BillNo { get; set; } + + [ProtoMember(10)] public byte[] AuthKey { get; set; } + + [ProtoMember(11, DataFormat = DataFormat.ZigZag)] public int SessionType { get; set; } + + [ProtoMember(12, DataFormat = DataFormat.ZigZag)] public int MsgType { get; set; } + + [ProtoMember(13, DataFormat = DataFormat.ZigZag)] public int EnvelOpeId { get; set; } + + [ProtoMember(14)] public byte[] Name { get; set; } + + [ProtoMember(15, DataFormat = DataFormat.ZigZag)] public int ConfType { get; set; } + + [ProtoMember(16, DataFormat = DataFormat.ZigZag)] public int MsgFrom { get; set; } + + [ProtoMember(17)] public byte[] PcBody { get; set; } + + [ProtoMember(18)] public byte[] Index { get; set; } + + [ProtoMember(19)] public uint RedChannel { get; set; } + + [ProtoMember(20)] public ulong[] GrapUin { get; set; } + + [ProtoMember(21)] public byte[] PbReserve { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs new file mode 100644 index 0000000..7856fa1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs @@ -0,0 +1,55 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +internal partial class QQWalletMsg +{ + [ProtoContract] + internal class QQWalletAioElem + { + [ProtoMember(1)] public uint Background { get; set; } + + [ProtoMember(2)] public uint Icon { get; set; } + + [ProtoMember(3)] public string Title { get; set; } + + [ProtoMember(4)] public string Subtitle { get; set; } + + [ProtoMember(5)] public string Content { get; set; } + + [ProtoMember(6)] public byte[] LinkUrl { get; set; } + + [ProtoMember(7)] public byte[] BlackStripe { get; set; } + + [ProtoMember(8)] public byte[] Notice { get; set; } + + [ProtoMember(9)] public uint TitleColor { get; set; } + + [ProtoMember(10)] public uint SubtitleColor { get; set; } + + [ProtoMember(11)] public byte[] ActionsPriority { get; set; } + + [ProtoMember(12)] public byte[] JumpUrl { get; set; } + + [ProtoMember(13)] public byte[] NativeIos { get; set; } + + [ProtoMember(14)] public byte[] NativeAndroid { get; set; } + + [ProtoMember(15)] public byte[] IconUrl { get; set; } + + [ProtoMember(16)] public uint ContentColor { get; set; } + + [ProtoMember(17)] public uint ContentBgColor { get; set; } + + [ProtoMember(18)] public byte[] AioImageLeft { get; set; } + + [ProtoMember(19)] public byte[] AioImageRight { get; set; } + + [ProtoMember(20)] public byte[] CftImage { get; set; } + + [ProtoMember(21)] public byte[] PbReserve { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.cs new file mode 100644 index 0000000..52cf8a2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/QQWalletMsg.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class QQWalletMsg +{ + [ProtoMember(1)] public QQWalletAioBody Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RedBagInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RedBagInfo.cs new file mode 100644 index 0000000..df12d53 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RedBagInfo.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class RedBagInfo +{ + [ProtoMember(1)] public uint? RedBagType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RichMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RichMsg.cs new file mode 100644 index 0000000..ba45519 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/RichMsg.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class RichMsg +{ + [ProtoMember(1)] public byte[]? Template1 { get; set; } + + [ProtoMember(2)] public int? ServiceId { get; set; } + + [ProtoMember(3)] public byte[]? MsgResId { get; set; } + + [ProtoMember(4)] public int? Rand { get; set; } + + [ProtoMember(5)] public int? Seq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs new file mode 100644 index 0000000..b07b6a4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +internal partial class SrcMsg +{ + [ProtoContract] + internal class Preserve + { + [ProtoMember(3)] public ulong MessageId { get; set; } + + [ProtoMember(6)] public string? SenderUid { get; set; } + + [ProtoMember(7)] public string? ReceiverUid { get; set; } + + [ProtoMember(8)] public uint? FriendSequence { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SrcMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SrcMsg.cs new file mode 100644 index 0000000..0fe9651 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/SrcMsg.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class SrcMsg +{ + [ProtoMember(1)] public List? OrigSeqs { get; set; } + + [ProtoMember(2)] public ulong SenderUin { get; set; } + + [ProtoMember(3)] public int? Time { get; set; } + + [ProtoMember(4)] public int? Flag { get; set; } + + [ProtoMember(5)] public List? Elems { get; set; } + + [ProtoMember(6)] public int? Type { get; set; } + + [ProtoMember(7)] public byte[]? RichMsg { get; set; } + + [ProtoMember(8)] public byte[]? PbReserve { get; set; } + + [ProtoMember(9)] public byte[]? SourceMsg { get; set; } // Metadata + + [ProtoMember(10)] public ulong? ToUin { get; set; } + + [ProtoMember(11)] public byte[]? TroopName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Text.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Text.cs new file mode 100644 index 0000000..b067d31 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/Text.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class Text +{ + [ProtoMember(1)] public string? Str { get; set; } + + [ProtoMember(2)] public string? Link { get; set; } + + [ProtoMember(3)] public byte[]? Attr6Buf { get; set; } + + [ProtoMember(4)] public byte[]? Attr7Buf { get; set; } + + [ProtoMember(11)] public byte[]? Buf { get; set; } + + [ProtoMember(12)] public byte[]? PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/TransElem.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/TransElem.cs new file mode 100644 index 0000000..11810bd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/TransElem.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class TransElem +{ + [ProtoMember(1)] public int ElemType { get; set; } + + [ProtoMember(2)] public byte[] ElemValue { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/VideoFile.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/VideoFile.cs new file mode 100644 index 0000000..2fe560e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Element/Implementation/VideoFile.cs @@ -0,0 +1,56 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class VideoFile +{ + [ProtoMember(1)] public string FileUuid { get; set; } + + [ProtoMember(2)] public byte[] FileMd5 { get; set; } + + [ProtoMember(3)] public string FileName { get; set; } + + [ProtoMember(4)] public int FileFormat { get; set; } + + [ProtoMember(5)] public int FileTime { get; set; } + + [ProtoMember(6)] public int FileSize { get; set; } + + [ProtoMember(7)] public int ThumbWidth { get; set; } + + [ProtoMember(8)] public int ThumbHeight { get; set; } + + [ProtoMember(9)] public byte[] ThumbFileMd5 { get; set; } + + [ProtoMember(10)] public byte[] Source { get; set; } + + [ProtoMember(11)] public int ThumbFileSize { get; set; } + + [ProtoMember(12)] public int BusiType { get; set; } + + [ProtoMember(13)] public int FromChatType { get; set; } + + [ProtoMember(14)] public int ToChatType { get; set; } + + [ProtoMember(15)] public bool BoolSupportProgressive { get; set; } + + [ProtoMember(16)] public int FileWidth { get; set; } + + [ProtoMember(17)] public int FileHeight { get; set; } + + [ProtoMember(18)] public int SubBusiType { get; set; } + + [ProtoMember(19)] public int VideoAttr { get; set; } + + [ProtoMember(20)] public byte[][] BytesThumbFileUrls { get; set; } + + [ProtoMember(21)] public byte[][] BytesVideoFileUrls { get; set; } + + [ProtoMember(22)] public int ThumbDownloadFlag { get; set; } + + [ProtoMember(23)] public int VideoDownloadFlag { get; set; } + + [ProtoMember(24)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/GroupRecallMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/GroupRecallMsg.cs new file mode 100644 index 0000000..ceddcbd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/GroupRecallMsg.cs @@ -0,0 +1,35 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message; + +/// +/// trpc.msg.msg_svc.MsgService.SsoGroupRecallMsg +/// +[ProtoContract] +internal class GroupRecallMsg +{ + [ProtoMember(1)] public uint Type { get; set; } // 1 + + [ProtoMember(2)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public GroupRecallMsgField3 Field3 { get; set; } + + [ProtoMember(4)] public GroupRecallMsgField4 Field4 { get; set; } +} + +[ProtoContract] +internal class GroupRecallMsgField3 +{ + [ProtoMember(1)] public uint Sequence { get; set; } + + [ProtoMember(2)] public uint Random { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } // 0 +} + +[ProtoContract] +internal class GroupRecallMsgField4 +{ + [ProtoMember(1)] public uint Field1 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Message.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Message.cs new file mode 100644 index 0000000..b9696d6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Message.cs @@ -0,0 +1,38 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +/// +/// MessageSvc.PbSendMsg for Send, trpc.msg.olpush.OlPushService.MsgPush for Receive +/// +[ProtoContract] +internal class Message +{ + [ProtoMember(1)] public RoutingHead? RoutingHead { get; set; } + + [ProtoMember(2)] public ContentHead? ContentHead { get; set; } + + [ProtoMember(3)] public MessageBody? Body { get; set; } + + [ProtoMember(4)] public uint? ClientSequence { get; set; } + + [ProtoMember(5)] public uint? Random { get; set; } + + [ProtoMember(6)] public byte[]? SyncCookie { get; set; } + + // [ProtoMember(7)] public AppShareInfo? AppShare { get; set; } + + [ProtoMember(8)] public uint? Via { get; set; } + + [ProtoMember(9)] public uint? DataStatist { get; set; } + + // [ProtoMember(10)] public MultiMsgAssist? MultiMsgAssist { get; set; } + + // [ProtoMember(11)] public InputNotifyInfo? InputNotifyInfo { get; set; } + + [ProtoMember(12)] public MessageControl? Ctrl { get; set; } + + // [ProtoMember(13)] public ReceiptReq? ReceiptReq { get; set; } + + [ProtoMember(14)] public uint MultiSendSeq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageBody.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageBody.cs new file mode 100644 index 0000000..eaf2b31 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageBody.cs @@ -0,0 +1,15 @@ +using Lagrange.Core.Internal.Packets.Message.Component; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class MessageBody +{ + [ProtoMember(1)] public RichText? RichText { get; set; } + + [ProtoMember(2)] public byte[]? MsgContent { get; set; } // Offline file is now put here(? + + [ProtoMember(3)] public byte[]? MsgEncryptContent { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageControl.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageControl.cs new file mode 100644 index 0000000..c6c1651 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/MessageControl.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class MessageControl +{ + [ProtoMember(1)] public int MsgFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendDeleteOrPinChanged.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendDeleteOrPinChanged.cs new file mode 100644 index 0000000..1a6f57a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendDeleteOrPinChanged.cs @@ -0,0 +1,48 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +// Stupid TX +// TODO: Currently only supports PinChanged +[ProtoContract] +internal class FriendDeleteOrPinChanged +{ + [ProtoMember(1)] public FriendDeleteOrPinChangedBody Body { get; set; } +} + +[ProtoContract] +internal class FriendDeleteOrPinChangedBody +{ + // Maybe is type, need check + // 7 Pin changed + // 5 Friend delete + [ProtoMember(2)] public uint Type { get; set; } + + [ProtoMember(20)] public PinChanged? PinChanged { get; set; } +} + +[ProtoContract] +internal class PinChanged +{ + [ProtoMember(1)] public PinChangedBody Body { get; set; } +} + +[ProtoContract] +internal class PinChangedBody +{ + [ProtoMember(1)] public string Uid { get; set; } + + [ProtoMember(2)] public uint? GroupUin { get; set; } + + [ProtoMember(400)] public PinChangedInfo Info { get; set; } +} + +[ProtoContract] +internal class PinChangedInfo +{ + // if (Timestamp.Length != 0) pin + // if (Timestamp.Length == 0) unpin + [ProtoMember(2)] public byte[] Timestamp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRecall.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRecall.cs new file mode 100644 index 0000000..e8a661b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRecall.cs @@ -0,0 +1,49 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class FriendRecall +{ + [ProtoMember(1)] public FriendRecallInfo Info { get; set; } + + [ProtoMember(2)] public uint InstId { get; set; } + + [ProtoMember(3)] public uint AppId { get; set; } + + [ProtoMember(4)] public uint LongMessageFlag { get; set; } + + [ProtoMember(5)] public byte[] Reserved { get; set; } +} + +[ProtoContract] +internal class FriendRecallInfo +{ + [ProtoMember(1)] public string FromUid { get; set; } + + [ProtoMember(2)] public string ToUid { get; set; } + + [ProtoMember(3)] public uint ClientSequence { get; set; } + + [ProtoMember(4)] public ulong NewId { get; set; } + + [ProtoMember(5)] public uint Time { get; set; } + + [ProtoMember(6)] public uint Random { get; set; } + + [ProtoMember(7)] public uint PkgNum { get; set; } + + [ProtoMember(8)] public uint PkgIndex { get; set; } + + [ProtoMember(9)] public uint DivSeq { get; set; } + + [ProtoMember(13)] public FriendRecallTipInfo TipInfo { get; set; } +} + +[ProtoContract] +internal class FriendRecallTipInfo +{ + [ProtoMember(2)] public string? Tip { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRequest.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRequest.cs new file mode 100644 index 0000000..841fd8d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/FriendRequest.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class FriendRequest +{ + [ProtoMember(1)] public FriendRequestInfo? Info { get; set; } +} + +[ProtoContract] +internal class FriendRequestInfo +{ + [ProtoMember(1)] public string TargetUid { get; set; } + + [ProtoMember(2)] public string SourceUid { get; set; } + + [ProtoMember(10)] public string Message { get; set; } + + [ProtoMember(11)] public string Source { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupAdmin.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupAdmin.cs new file mode 100644 index 0000000..fa914d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupAdmin.cs @@ -0,0 +1,33 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupAdmin +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Flag { get; set; } + + [ProtoMember(3)] public bool IsPromote { get; set; } + + [ProtoMember(4)] public GroupAdminBody Body { get; set; } +} + +[ProtoContract] +internal class GroupAdminBody +{ + [ProtoMember(1)] public GroupAdminExtra? ExtraDisable { get; set; } + + [ProtoMember(2)] public GroupAdminExtra? ExtraEnable { get; set; } +} + +[ProtoContract] +internal class GroupAdminExtra +{ + [ProtoMember(1)] public string AdminUid { get; set; } + + [ProtoMember(2)] public bool IsPromote { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupChange.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupChange.cs new file mode 100644 index 0000000..100b4a3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupChange.cs @@ -0,0 +1,43 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupChange +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Flag { get; set; } + + [ProtoMember(3)] public string MemberUid { get; set; } + + [ProtoMember(4)] public uint DecreaseType { get; set; } // 131 Kick 130 Exit + + [ProtoMember(5)] public byte[]? Operator { get; set; } + + [ProtoMember(6)] public uint IncreaseType { get; set; } + + [ProtoMember(7)] public byte[]? Field7 { get; set; } +} + +[ProtoContract] +internal class OperatorInfo +{ + [ProtoMember(1)] public OperatorField1 Operator { get; set; } +} + +[ProtoContract] +internal class OperatorField1 +{ + [ProtoMember(1)] public string? Uid { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public byte[]? Field3 { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(5)] public byte[]? Field5 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupEssenceMessage.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupEssenceMessage.cs new file mode 100644 index 0000000..0875147 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupEssenceMessage.cs @@ -0,0 +1,30 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; +#pragma warning disable CS8618 + +[ProtoContract] +internal class EssenceMessage +{ + [ProtoMember(1)] public uint GroupUin; + + [ProtoMember(2)] public uint MsgSequence; + + [ProtoMember(3)] public uint Random; + + [ProtoMember(4)] public uint SetFlag; // set 1 remove 2 + + [ProtoMember(5)] public uint MemberUin; + + [ProtoMember(6)] public uint OperatorUin; + + [ProtoMember(7)] public uint TimeStamp; + + [ProtoMember(8)] public uint MsgSequence2; // removed 0 + + [ProtoMember(9)] public string OperatorNickName; + + [ProtoMember(10)] public string MemberNickName; + + [ProtoMember(11)] public uint SetFlag2; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvitation.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvitation.cs new file mode 100644 index 0000000..3b71b88 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvitation.cs @@ -0,0 +1,47 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupInvitation +{ + [ProtoMember(1)] public int Cmd { get; set; } + + [ProtoMember(2)] public InvitationInfo Info { get; set; } +} + +[ProtoContract] +internal class InvitationInfo +{ + [ProtoMember(1)] public InvitationInner Inner { get; set; } +} + + +[ProtoContract] +internal class InvitationInner +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(5)] public string TargetUid { get; set; } + + [ProtoMember(6)] public string InvitorUid { get; set; } + + [ProtoMember(7)] public uint Field7 { get; set; } + + [ProtoMember(9)] public uint Field9 { get; set; } + + [ProtoMember(10)] public byte[] Field10 { get; set; } + + [ProtoMember(11)] public uint Field11 { get; set; } + + [ProtoMember(12)] public string Field12 { get; set; } +} + diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvite.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvite.cs new file mode 100644 index 0000000..061c0a1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupInvite.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupInvite +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } // 1 + + [ProtoMember(3)] public uint Field3 { get; set; } // 4 + + [ProtoMember(4)] public uint Field4 { get; set; } // 0 + + [ProtoMember(5)] public string InvitorUid { get; set; } + + [ProtoMember(6)] public byte[] Hashes { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupJoin.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupJoin.cs new file mode 100644 index 0000000..7ac6542 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupJoin.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupJoin +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public string TargetUid { get; set; } = string.Empty; + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(6)] public uint Field6 { get; set; } + + [ProtoMember(7)] public string Field7 { get; set; } = string.Empty; + + [ProtoMember(8)] public uint Field8 { get; set; } + + [ProtoMember(9)] public byte[] Field9 { get; set; } = Array.Empty(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs new file mode 100644 index 0000000..2e08ae5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs @@ -0,0 +1,62 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupMemberEnter +{ + [ProtoMember(1)] public bool Field1 { get; set; } + + [ProtoMember(2)] public GroupMemberEnterContentBody Body { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterContentBody +{ + [ProtoMember(1)] public GroupMemberEnterContentBodyField1 Field1 { get; set; } + + [ProtoMember(2)] public uint GroupId { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public GroupMemberEnterInfo Info { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterContentBodyField1 +{ + [ProtoMember(1)] public uint Field1 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterInfo +{ + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public GroupMemberEnterDetail Detail { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterDetail +{ + [ProtoMember(3)] public uint GroupMemberUin { get; set; } + [ProtoMember(4)] public uint GroupId { get; set; } + [ProtoMember(5)] public uint Field5 { get; set; } + [ProtoMember(6)] public uint Field6 { get; set; } + [ProtoMember(20)] public GroupMemberEnterStyle Style { get; set; } + [ProtoMember(21)] public uint Field21 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterStyle +{ + [ProtoMember(1)] public uint StyleId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMute.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMute.cs new file mode 100644 index 0000000..e736589 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupMute.cs @@ -0,0 +1,37 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupMute +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint SubType { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public string? OperatorUid { get; set; } + + [ProtoMember(5)] public GroupMuteData Data { get; set; } +} + +[ProtoContract] +internal class GroupMuteData +{ + [ProtoMember(1)] public uint Timestamp { get; set; } + + [ProtoMember(2)] public uint Type { get; set; } + + [ProtoMember(3)] public GroupMuteState State { get; set; } +} + +[ProtoContract] +internal class GroupMuteState +{ + [ProtoMember(1)] public string? TargetUid { get; set; } + + [ProtoMember(2)] public uint Duration { get; set; } // uint.MaxValue = Mute else Lift +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupNameChange.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupNameChange.cs new file mode 100644 index 0000000..3e8ec68 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupNameChange.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupNameChange +{ + [ProtoMember(2)] public string Name { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupReaction.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupReaction.cs new file mode 100644 index 0000000..5ba4bdd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupReaction.cs @@ -0,0 +1,45 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupReactionData0 +{ + // What the fuck was tx thinking? + [ProtoMember(1)] public GroupReactionData1 Data { get; set; } +} + +[ProtoContract] +internal class GroupReactionData1 +{ + [ProtoMember(1)] public GroupReactionData2 Data { get; set; } +} + +[ProtoContract] +internal class GroupReactionData2 +{ + [ProtoMember(2)] public GroupReactionTarget Target { get; set; } + + [ProtoMember(3)] public GroupReactionData3 Data { get; set; } +} + +[ProtoContract] +internal class GroupReactionTarget +{ + [ProtoMember(1)] public uint Sequence { get; set; } +} + +[ProtoContract] +internal class GroupReactionData3 +{ + [ProtoMember(1)] public string Code { get; set; } + + [ProtoMember(3)] public uint Count { get; set; } + + [ProtoMember(4)] public string OperatorUid { get; set; } + + [ProtoMember(5)] public uint Type { get; set; } // 1 Add 2 Remove +} + diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupRecall.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupRecall.cs new file mode 100644 index 0000000..76c8da3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/GroupRecall.cs @@ -0,0 +1,43 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupRecall +{ + [ProtoMember(1)] public string? OperatorUid { get; set; } + + [ProtoMember(3)] public List RecallMessages { get; set; } + + [ProtoMember(5)] public byte[] UserDef { get; set; } + + [ProtoMember(6)] public int GroupType { get; set; } + + [ProtoMember(7)] public int OpType { get; set; } + + [ProtoMember(9)] public GroupRecallTipInfo? TipInfo { get; set; } +} + +[ProtoContract] +internal class RecallMessage +{ + [ProtoMember(1)] public uint Sequence { get; set; } + + [ProtoMember(2)] public uint Time { get; set; } + + [ProtoMember(3)] public uint Random { get; set; } + + [ProtoMember(4)] public uint Type { get; set; } + + [ProtoMember(5)] public uint Flag { get; set; } + + [ProtoMember(6)] public string AuthorUid { get; set; } +} + +[ProtoContract] +internal class GroupRecallTipInfo +{ + [ProtoMember(2)] public string Tip { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/NotifyMessageBody.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/NotifyMessageBody.cs new file mode 100644 index 0000000..067abbd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Notify/NotifyMessageBody.cs @@ -0,0 +1,62 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +#pragma warning disable CS8618 + +/// +/// group0x857.proto MessageRecallReminder +/// +[ProtoContract] +internal class NotifyMessageBody +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(4)] public uint GroupUin { get; set; } + + [ProtoMember(5)] public byte[]? EventParam { get; set; } + + [ProtoMember(11)] public GroupRecall Recall { get; set; } + + [ProtoMember(13)] public uint? Field13 { get; set; } + + [ProtoMember(21)] public string OperatorUid { get; set; } + + [ProtoMember(26)] public GeneralGrayTipInfo GeneralGrayTip { get; set; } + + [ProtoMember(33)] public EssenceMessage EssenceMessage; + + [ProtoMember(37)] public uint MsgSequence { get; set; } + + [ProtoMember(39)] public uint Field39 { get; set; } + + [ProtoMember(44)] public GroupReactionData0 Reaction { get; set; } +} + +[ProtoContract] +internal class GeneralGrayTipInfo +{ + [ProtoMember(1)] public ulong BusiType { get; set; } + + [ProtoMember(2)] public ulong BusiId { get; set; } + + [ProtoMember(3)] public uint CtrlFlag { get; set; } + + [ProtoMember(4)] public uint C2CType { get; set; } + + [ProtoMember(5)] public uint ServiceType { get; set; } + + [ProtoMember(6)] public ulong TemplId { get; set; } + + [ProtoMember(7)] public TemplParam[] MsgTemplParam { get; set; } + + [ProtoMember(8)] public string Content { get; set; } +} + +[ProtoContract] +internal class TemplParam +{ + [ProtoMember(1)] public string Name { get; set; } + + [ProtoMember(2)] public string Value { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/PushMsg.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/PushMsg.cs new file mode 100644 index 0000000..957781d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/PushMsg.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Internal.Packets.System; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class PushMsg +{ + [ProtoMember(1)] public PushMsgBody Message { get; set; } + + [ProtoMember(3)] public int? Status { get; set; } + + [ProtoMember(4)] public NTSysEvent? NtEvent { get; set; } + + [ProtoMember(5)] public int? PingFLag { get; set; } + + [ProtoMember(9)] public int? GeneralFlag { get; set; } +} + +[ProtoContract] +internal class PushMsgBody +{ + [ProtoMember(1)] public ResponseHead ResponseHead { get; set; } + + [ProtoMember(2)] public ContentHead ContentHead { get; set; } + + [ProtoMember(3)] public MessageBody? Body { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ResponseHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ResponseHead.cs new file mode 100644 index 0000000..ffed747 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/ResponseHead.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Internal.Packets.Message.Routing; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class ResponseHead +{ + [ProtoMember(1)] public uint FromUin { get; set; } + + [ProtoMember(2)] public string? FromUid { get; set; } + + [ProtoMember(3)] public uint Type { get; set; } + + [ProtoMember(4)] public uint SigMap { get; set; } // 鬼知道是啥 + + [ProtoMember(5)] public uint ToUin { get; set; } + + [ProtoMember(6)] public string? ToUid { get; set; } + + [ProtoMember(7)] public ResponseForward? Forward { get; set; } + + [ProtoMember(8)] public ResponseGrp? Grp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ForwardHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ForwardHead.cs new file mode 100644 index 0000000..a702c86 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ForwardHead.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class ForwardHead +{ + [ProtoMember(1)] public uint? Field1 { get; set; } // 0 + + [ProtoMember(2)] public uint? Field2 { get; set; } // 0 + + [ProtoMember(3)] public uint? Field3 { get; set; } // for friend: 2, for group: null + + [ProtoMember(4)] public string? UnknownBase64 { get; set; } + + [ProtoMember(5)] public string? Avatar { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Grp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Grp.cs new file mode 100644 index 0000000..c3fc0a0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Grp.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class Grp +{ + [ProtoMember(1)] public uint? GroupCode { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/GrpTmp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/GrpTmp.cs new file mode 100644 index 0000000..57fbe4a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/GrpTmp.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class GrpTmp +{ + [ProtoMember(1)] public uint? GroupUin { get; set; } + + [ProtoMember(2)] public uint? ToUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseForward.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseForward.cs new file mode 100644 index 0000000..26d2037 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseForward.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class ResponseForward +{ + [ProtoMember(6)] public string? FriendName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseGrp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseGrp.cs new file mode 100644 index 0000000..b2c6251 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/ResponseGrp.cs @@ -0,0 +1,16 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class ResponseGrp +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(4)] public string MemberName { get; set; } + + [ProtoMember(5)] public uint Unknown5 { get; set; } + + [ProtoMember(7)] public string GroupName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Trans0X211.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Trans0X211.cs new file mode 100644 index 0000000..7cf659b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/Trans0X211.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class Trans0X211 +{ + [ProtoMember(1)] public ulong? ToUin { get; set; } + + [ProtoMember(2)] public uint? CcCmd { get; set; } + + [ProtoMember(8)] public string? Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/WPATmp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/WPATmp.cs new file mode 100644 index 0000000..69ddf55 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/Routing/WPATmp.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Routing; + +[ProtoContract] +internal class WPATmp +{ + [ProtoMember(1)] public ulong ToUin { get; set; } + + [ProtoMember(2)] public byte[] Sig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/RoutingHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/RoutingHead.cs new file mode 100644 index 0000000..278c966 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/RoutingHead.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Internal.Packets.Message.Routing; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +[ProtoContract] +internal class RoutingHead +{ + [ProtoMember(1)] public C2C.C2C? C2C { get; set; } + + [ProtoMember(2)] public Grp? Grp { get; set; } + + [ProtoMember(3)] public GrpTmp? GrpTmp { get; set; } + + [ProtoMember(6)] public WPATmp? WpaTmp { get; set; } + + [ProtoMember(15)] public Trans0X211? Trans0X211 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/SsoReadedReport.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/SsoReadedReport.cs new file mode 100644 index 0000000..0ef6259 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Message/SsoReadedReport.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Message; + +/// +/// trpc.msg.msg_svc.MsgService.SsoReadedReport +/// +[ProtoContract] +internal class SsoReadedReport +{ + [ProtoMember(1)] public SsoReadedReportGroup? Group { get; set; } + + [ProtoMember(2)] public SsoReadedReportC2C? C2C { get; set; } +} + +[ProtoContract] +internal class SsoReadedReportC2C +{ + [ProtoMember(2)] public string? TargetUid { get; set; } + + [ProtoMember(3)] public uint Time { get; set; } + + [ProtoMember(4)] public uint StartSequence { get; set; } +} + +[ProtoContract] +internal class SsoReadedReportGroup +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint StartSequence { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/DataHighwayHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/DataHighwayHead.cs new file mode 100644 index 0000000..6033d7b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/DataHighwayHead.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +[ProtoContract] +internal class DataHighwayHead +{ + [ProtoMember(1)] public uint Version { get; set; } + + [ProtoMember(2)] public string? Uin { get; set; } + + [ProtoMember(3)] public string? Command { get; set; } + + [ProtoMember(4)] public uint Seq { get; set; } + + [ProtoMember(5)] public uint RetryTimes { get; set; } + + [ProtoMember(6)] public uint AppId { get; set; } + + [ProtoMember(7)] public uint DataFlag { get; set; } + + [ProtoMember(8)] public uint CommandId { get; set; } + + [ProtoMember(9)] public byte[]? BuildVer { get; set; } + + // [ProtoMember(10)] public uint LocaleId { get; set; } + + // [ProtoMember(11)] public uint EnvId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/FileUploadExt.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/FileUploadExt.cs new file mode 100644 index 0000000..5985b75 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/FileUploadExt.cs @@ -0,0 +1,103 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class FileUploadExt +{ + [ProtoMember(1)] public int Unknown1 { get; set; } + + [ProtoMember(2)] public int Unknown2 { get; set; } + + [ProtoMember(3)] public int Unknown3 { get; set; } + + [ProtoMember(100)] public FileUploadEntry Entry { get; set; } + + [ProtoMember(200)] public int Unknown200 { get; set; } +} + +[ProtoContract] +internal class FileUploadEntry +{ + [ProtoMember(100)] public ExcitingBusiInfo BusiBuff { get; set; } + + [ProtoMember(200)] public ExcitingFileEntry FileEntry { get; set; } + + [ProtoMember(300)] public ExcitingClientInfo ClientInfo { get; set; } + + [ProtoMember(400)] public ExcitingFileNameInfo FileNameInfo { get; set; } + + [ProtoMember(500)] public ExcitingHostConfig Host { get; set; } +} + +[ProtoContract] +internal class ExcitingBusiInfo +{ + [ProtoMember(1)] public int BusId { get; set; } + + [ProtoMember(100)] public long SenderUin { get; set; } + + [ProtoMember(200)] public long ReceiverUin { get; set; } + + [ProtoMember(400)] public long GroupCode { get; set; } +} + +[ProtoContract] +internal class ExcitingFileEntry +{ + [ProtoMember(100)] public long FileSize { get; set; } + + [ProtoMember(200)] public byte[] Md5 { get; set; } + + [ProtoMember(300)] public byte[] CheckKey { get; set; } + + [ProtoMember(400)] public byte[] Md5S2 { get; set; } + + [ProtoMember(600)] public string FileId { get; set; } + + [ProtoMember(700)] public byte[] UploadKey { get; set; } +} + +[ProtoContract] +internal class ExcitingClientInfo +{ + [ProtoMember(100)] public int ClientType { get; set; } + + [ProtoMember(200)] public string AppId { get; set; } + + [ProtoMember(300)] public int TerminalType { get; set; } + + [ProtoMember(400)] public string ClientVer { get; set; } + + [ProtoMember(600)] public int Unknown { get; set; } +} + +[ProtoContract] +internal class ExcitingFileNameInfo +{ + [ProtoMember(100)] public string FileName { get; set; } +} + +[ProtoContract] +internal class ExcitingHostConfig +{ + [ProtoMember(200)] public List Hosts { get; set; } +} + +[ProtoContract] +internal class ExcitingHostInfo +{ + [ProtoMember(1)] public ExcitingUrlInfo Url { get; set; } + + [ProtoMember(2)] public uint Port { get; set; } +} + +[ProtoContract] +internal class ExcitingUrlInfo +{ + [ProtoMember(1)] public int Unknown { get; set; } + + [ProtoMember(2)] public string Host { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/GroupAvatarExtra.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/GroupAvatarExtra.cs new file mode 100644 index 0000000..abedfe4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/GroupAvatarExtra.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; +#pragma warning disable CS8618 + +[ProtoContract] +internal class GroupAvatarExtra +{ + [ProtoMember(1)] public uint Type { get; set; } // 101 + + [ProtoMember(2)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public GroupAvatarExtraField3 Field3 { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } // 3 + + [ProtoMember(6)] public uint Field6 { get; set; } // 1 +} + +[ProtoContract] +internal class GroupAvatarExtraField3 +{ + [ProtoMember(1)] public uint Field1 { get; set; } // 1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/LoginSigHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/LoginSigHead.cs new file mode 100644 index 0000000..63b1102 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/LoginSigHead.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +[ProtoContract] +internal class LoginSigHead +{ + [ProtoMember(1)] public uint Uint32LoginSigType { get; set; } + + [ProtoMember(2)] public byte[] BytesLoginSig { get; set; } = Array.Empty(); + + [ProtoMember(3)] public uint AppId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/NTV2RichMediaHighwayExt.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/NTV2RichMediaHighwayExt.cs new file mode 100644 index 0000000..718068c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/NTV2RichMediaHighwayExt.cs @@ -0,0 +1,52 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +//Resharper Disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class NTV2RichMediaHighwayExt +{ + [ProtoMember(1)] public string FileUuid { get; set; } + + [ProtoMember(2)] public string UKey { get; set; } + + [ProtoMember(5)] public NTHighwayNetwork Network { get; set; } + + [ProtoMember(6)] public List MsgInfoBody { get; set; } + + [ProtoMember(10)] public uint BlockSize { get; set; } + + [ProtoMember(11)] public NTHighwayHash Hash { get; set; } +} + +[ProtoContract] +internal class NTHighwayHash +{ + [ProtoMember(1)] public List FileSha1 { get; set; } +} + +[ProtoContract] +internal class NTHighwayNetwork +{ + [ProtoMember(1)] public List IPv4s { get; set; } +} + + +[ProtoContract] +internal class NTHighwayIPv4 +{ + [ProtoMember(1)] public NTHighwayDomain Domain { get; set; } + + [ProtoMember(2)] public uint Port { get; set; } +} + +[ProtoContract] +internal class NTHighwayDomain +{ + [ProtoMember(1)] public bool IsEnable { get; set; } // true + + [ProtoMember(2)] public string IP { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/ReqDataHighwayHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/ReqDataHighwayHead.cs new file mode 100644 index 0000000..87a4a2c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/ReqDataHighwayHead.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +[ProtoContract] +internal class ReqDataHighwayHead +{ + [ProtoMember(1)] public DataHighwayHead? MsgBaseHead { get; set; } + + [ProtoMember(2)] public SegHead? MsgSegHead { get; set; } + + [ProtoMember(3)] public byte[]? BytesReqExtendInfo { get; set; } + + [ProtoMember(4)] public ulong Timestamp { get; set; } + + [ProtoMember(5)] public LoginSigHead? MsgLoginSigHead { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/RespDataHighwayHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/RespDataHighwayHead.cs new file mode 100644 index 0000000..fbc53d1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/RespDataHighwayHead.cs @@ -0,0 +1,27 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +[ProtoContract] +internal class RespDataHighwayHead +{ + [ProtoMember(1)] public DataHighwayHead? MsgBaseHead { get; set; } + + [ProtoMember(2)] public SegHead? MsgSegHead { get; set; } + + [ProtoMember(3)] public uint ErrorCode { get; set; } + + [ProtoMember(4)] public uint AllowRetry { get; set; } + + [ProtoMember(5)] public uint CacheCost { get; set; } + + [ProtoMember(6)] public uint HtCost { get; set; } + + [ProtoMember(7)] public byte[]? BytesRspExtendInfo { get; set; } + + [ProtoMember(8)] public ulong Timestamp { get; set; } + + [ProtoMember(9)] public ulong Range { get; set; } + + [ProtoMember(10)] public uint IsReset { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/SegHead.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/SegHead.cs new file mode 100644 index 0000000..fa850ca --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Highway/SegHead.cs @@ -0,0 +1,33 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Highway; + +[ProtoContract] +internal class SegHead +{ + [ProtoMember(1)] public uint ServiceId { get; set; } + + [ProtoMember(2)] public ulong Filesize { get; set; } + + [ProtoMember(3)] public ulong DataOffset { get; set; } + + [ProtoMember(4)] public uint DataLength { get; set; } + + [ProtoMember(5)] public uint RetCode { get; set; } + + [ProtoMember(6)] public byte[] ServiceTicket { get; set; } = Array.Empty(); + + // [ProtoMember(7)] public uint Flag { get; set; } + + [ProtoMember(8)] public byte[] Md5 { get; set; } = Array.Empty(); + + [ProtoMember(9)] public byte[] FileMd5 { get; set; } = Array.Empty(); + + [ProtoMember(10)] public uint CacheAddr { get; set; } + + // [ProtoMember(11)] public uint QueryTimes { get; set; } + + // [ProtoMember(12)] public uint UpdateCacheIp { get; set; } + + [ProtoMember(13)] public uint CachePort { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs new file mode 100644 index 0000000..793d67b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaReq.cs @@ -0,0 +1,389 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +// Resharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class NTV2RichMediaReq +{ + [ProtoMember(1)] public MultiMediaReqHead ReqHead { get; set; } + + [ProtoMember(2)] public UploadReq Upload { get; set; } + + [ProtoMember(3)] public DownloadReq Download { get; set; } + + [ProtoMember(4)] public DownloadRKeyReq DownloadRKey { get; set; } + + [ProtoMember(5)] public DeleteReq Delete { get; set; } + + [ProtoMember(6)] public UploadCompletedReq UploadCompleted { get; set; } + + [ProtoMember(7)] public MsgInfoAuthReq MsgInfoAuth { get; set; } + + [ProtoMember(8)] public UploadKeyRenewalReq UploadKeyRenewal { get; set; } + + [ProtoMember(9)] public DownloadSafeReq DownloadSafe { get; set; } + + [ProtoMember(99)] public byte[]? Extension { get; set; } +} + +[ProtoContract] +internal class MultiMediaReqHead +{ + [ProtoMember(1)] public CommonHead Common { get; set; } + + [ProtoMember(2)] public SceneInfo Scene { get; set; } + + [ProtoMember(3)] public ClientMeta Client { get; set; } +} + +[ProtoContract] +internal class CommonHead +{ + [ProtoMember(1)] public uint RequestId { get; set; } // 1 + + [ProtoMember(2)] public uint Command { get; set; } // 200 +} + +[ProtoContract] +internal class SceneInfo +{ + [ProtoMember(101)] public uint RequestType { get; set; } // 1 + + [ProtoMember(102)] public uint BusinessType { get; set; } // 3 + + [ProtoMember(200)] public uint SceneType { get; set; } // 1 + + [ProtoMember(201)] public C2CUserInfo? C2C { get; set; } + + + [ProtoMember(202)] public GroupInfo? Group { get; set; } +} + +[ProtoContract] +internal class C2CUserInfo +{ + [ProtoMember(1)] public uint AccountType { get; set; } // 2 + + [ProtoMember(2)] public string TargetUid { get; set; } +} + + +[ProtoContract] +internal class GroupInfo +{ + [ProtoMember(1)] public uint GroupUin { get; set; } +} + +[ProtoContract] +internal class ClientMeta +{ + [ProtoMember(1)] public uint AgentType { get; set; } // 2 +} + +[ProtoContract] +internal class DownloadReq +{ + [ProtoMember(1)] public IndexNode Node { get; set; } + + [ProtoMember(2)] public DownloadExt Download { get; set; } +} + +[ProtoContract] +internal class IndexNode +{ + [ProtoMember(1)] public FileInfo Info { get; set; } + + [ProtoMember(2)] public string FileUuid { get; set; } + + [ProtoMember(3)] public uint StoreId { get; set; } // 0旧服务器 1为nt服务器 + + [ProtoMember(4)] public uint UploadTime { get; set; } // 0 + + [ProtoMember(5)] public uint Ttl { get; set; } // 0 + + [ProtoMember(6)] public uint SubType { get; set; } // 0 +} + +[ProtoContract] +internal class FileInfo +{ + [ProtoMember(1)] public uint FileSize { get; set; } // 0 + + [ProtoMember(2)] public string FileHash { get; set; } + + [ProtoMember(3)] public string FileSha1 { get; set; } // "" + + [ProtoMember(4)] public string FileName { get; set; } + + [ProtoMember(5)] public FileType Type { get; set; } + + [ProtoMember(6)] public uint Width { get; set; } // 0 + + [ProtoMember(7)] public uint Height { get; set; } // 0 + + [ProtoMember(8)] public uint Time { get; set; } // 2 + + [ProtoMember(9)] public uint Original { get; set; } // 0 +} + +[ProtoContract] +internal class FileType +{ + [ProtoMember(1)] public uint Type { get; set; } // 2 + + [ProtoMember(2)] public uint PicFormat { get; set; } // 0 + + [ProtoMember(3)] public uint VideoFormat { get; set; } // 0 + + [ProtoMember(4)] public uint VoiceFormat { get; set; } // 1 +} + +[ProtoContract] +internal class DownloadExt +{ + [ProtoMember(1)] public PicDownloadExt Pic { get; set; } + + [ProtoMember(2)] public VideoDownloadExt Video { get; set; } + + [ProtoMember(3)] public PttDownloadExt Ptt { get; set; } +} + +[ProtoContract] +internal class VideoDownloadExt +{ + [ProtoMember(1)] public uint BusiType { get; set; } // 0 + + [ProtoMember(2)] public uint SceneType { get; set; } // 0 + + [ProtoMember(3)] public uint SubBusiType { get; set; } // 0 +} + +[ProtoContract] +internal class PicDownloadExt { } + +[ProtoContract] +internal class PttDownloadExt { } + +[ProtoContract] +internal class PicUrlExtInfo +{ + [ProtoMember(1)] public string OriginalParameter { get; set; } + + [ProtoMember(2)] public string BigParameter { get; set; } + + [ProtoMember(3)] public string ThumbParameter { get; set; } +} + +[ProtoContract] +internal class VideoExtInfo +{ + [ProtoMember(1)] public uint VideoCodecFormat { get; set; } +} + +[ProtoContract] +internal class MsgInfo +{ + [ProtoMember(1)] public List MsgInfoBody { get; set; } + + [ProtoMember(2)] public ExtBizInfo ExtBizInfo { get; set; } +} + +[ProtoContract] +internal class MsgInfoBody +{ + [ProtoMember(1)] public IndexNode Index { get; set; } + + [ProtoMember(2)] public PictureInfo Picture { get; set; } + + [ProtoMember(3)] public VideoInfo Video { get; set; } + + [ProtoMember(4)] public AudioInfo Audio { get; set; } + + [ProtoMember(5)] public bool FileExist { get; set; } + + [ProtoMember(6)] public HashSum HashSum { get; set; } +} + +[ProtoContract] +public class HashSum +{ + [ProtoMember(201)] public C2cSource BytesPbReserveC2c; + + [ProtoMember(202)] public TroopSource? TroopSource; +} + +[ProtoContract] +public class C2cSource +{ + [ProtoMember(2)] public string FriendUid; +} + +[ProtoContract] +public class TroopSource +{ + [ProtoMember(1)] public uint GroupUin; +} + +[ProtoContract] +internal class VideoInfo { } + +[ProtoContract] +internal class AudioInfo { } + +[ProtoContract] +internal class PictureInfo +{ + [ProtoMember(1)] public string UrlPath { get; set; } + + [ProtoMember(2)] public PicUrlExtInfo Ext { get; set; } + + [ProtoMember(3)] public string Domain { get; set; } +} + + +[ProtoContract] +internal class ExtBizInfo +{ + [ProtoMember(1)] public PicExtBizInfo Pic { get; set; } + + [ProtoMember(2)] public VideoExtBizInfo Video { get; set; } + + [ProtoMember(3)] public PttExtBizInfo Ptt { get; set; } + + [ProtoMember(10)] public uint BusiType { get; set; } +} + +[ProtoContract] +internal class PttExtBizInfo +{ + [ProtoMember(1)] public ulong SrcUin { get; set; } + + [ProtoMember(2)] public uint PttScene { get; set; } + + [ProtoMember(3)] public uint PttType { get; set; } + + [ProtoMember(4)] public uint ChangeVoice { get; set; } + + [ProtoMember(5)] public byte[] Waveform { get; set; } + + [ProtoMember(6)] public uint AutoConvertText { get; set; } + + [ProtoMember(11)] public byte[] BytesReserve { get; set; } + + [ProtoMember(12)] public byte[] BytesPbReserve { get; set; } + + [ProtoMember(13)] public byte[] BytesGeneralFlags { get; set; } +} + +[ProtoContract] +internal class VideoExtBizInfo +{ + [ProtoMember(1)] public uint FromScene { get; set; } + + [ProtoMember(2)] public uint ToScene { get; set; } + + [ProtoMember(3)] public byte[] BytesPbReserve { get; set; } +} + +[ProtoContract] +internal class PicExtBizInfo +{ + [ProtoMember(1)] public uint BizType { get; set; } + + [ProtoMember(2)] public string TextSummary { get; set; } + + [ProtoMember(11)] public byte[]? BytesPbReserveC2c { get; set; } + + [ProtoMember(12)] public byte[]? BytesPbReserveTroop { get; set; } + + [ProtoMember(1001)] public uint FromScene { get; set; } + + [ProtoMember(1002)] public uint ToScene { get; set; } + + [ProtoMember(1003)] public uint OldFileId { get; set; } +} + +[ProtoContract] +internal class DownloadSafeReq +{ + [ProtoMember(1)] public IndexNode Index { get; set; } +} + +[ProtoContract] +internal class UploadKeyRenewalReq +{ + [ProtoMember(1)] public string OldUKey { get; set; } + + [ProtoMember(2)] public uint SubType { get; set; } +} + +[ProtoContract] +internal class MsgInfoAuthReq +{ + [ProtoMember(1)] public byte[] Msg { get; set; } + + [ProtoMember(2)] public ulong AuthTime { get; set; } +} + +[ProtoContract] +internal class UploadCompletedReq +{ + [ProtoMember(1)] public bool SrvSendMsg { get; set; } + + [ProtoMember(2)] public ulong ClientRandomId { get; set; } + + [ProtoMember(3)] public MsgInfo MsgInfo { get; set; } + + [ProtoMember(4)] public uint ClientSeq { get; set; } +} + +[ProtoContract] +internal class DeleteReq +{ + [ProtoMember(1)] public List Index { get; set; } + + [ProtoMember(2)] public bool NeedRecallMsg { get; set; } + + [ProtoMember(3)] public ulong MsgSeq { get; set; } + + [ProtoMember(4)] public ulong MsgRandom { get; set; } + + [ProtoMember(5)] public ulong MsgTime { get; set; } +} + +[ProtoContract] +internal class DownloadRKeyReq +{ + [ProtoMember(1)] public List Types { get; set; } +} + +[ProtoContract] +internal class UploadInfo +{ + [ProtoMember(1)] public FileInfo FileInfo { get; set; } + + [ProtoMember(2)] public uint SubFileType { get; set; } +} + +[ProtoContract] +internal class UploadReq +{ + [ProtoMember(1)] public List UploadInfo { get; set; } + + [ProtoMember(2)] public bool TryFastUploadCompleted { get; set; } + + [ProtoMember(3)] public bool SrvSendMsg { get; set; } + + [ProtoMember(4)] public ulong ClientRandomId { get; set; } + + [ProtoMember(5)] public uint CompatQMsgSceneType { get; set; } + + [ProtoMember(6)] public ExtBizInfo ExtBizInfo { get; set; } + + [ProtoMember(7)] public uint ClientSeq { get; set; } + + [ProtoMember(8)] public bool NoNeedCompatMsg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaResp.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaResp.cs new file mode 100644 index 0000000..1e71940 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Common/NTV2RichMediaResp.cs @@ -0,0 +1,194 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Common; + +// Resharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class NTV2RichMediaResp +{ + [ProtoMember(1)] public MultiMediaRespHead RespHead { get; set; } + + [ProtoMember(2)] public UploadResp Upload { get; set; } + + [ProtoMember(3)] public DownloadResp Download { get; set; } + + [ProtoMember(4)] public DownloadRKeyResp DownloadRKey { get; set; } + + [ProtoMember(5)] public DeleteResp Delete { get; set; } + + [ProtoMember(6)] public UploadCompletedResp UploadCompleted { get; set; } + + [ProtoMember(7)] public MsgInfoAuthResp MsgInfoAuth { get; set; } + + [ProtoMember(8)] public UploadKeyRenewalResp UploadKeyRenewal { get; set; } + + [ProtoMember(9)] public DownloadSafeResp DownloadSafe { get; set; } + + [ProtoMember(99)] public byte[]? Extension { get; set; } +} + +[ProtoContract] +internal class MultiMediaRespHead +{ + [ProtoMember(1)] public CommonHead Common { get; set; } + + [ProtoMember(2)] public uint RetCode { get; set; } + + [ProtoMember(3)] public string Message { get; set; } +} + +[ProtoContract] +internal class DownloadResp +{ + [ProtoMember(1)] public string RKeyParam { get; set; } + + [ProtoMember(2)] public uint RKeyTtlSecond { get; set; } + + [ProtoMember(3)] public DownloadInfo Info { get; set; } + + [ProtoMember(4)] public uint RKeyCreateTime { get; set; } +} + +[ProtoContract] +internal class DownloadInfo +{ + [ProtoMember(1)] public string Domain { get; set; } + + [ProtoMember(2)] public string UrlPath { get; set; } + + [ProtoMember(3)] public uint HttpsPort { get; set; } + + [ProtoMember(4)] public List IPv4s { get; set; } + + [ProtoMember(5)] public List IPv6s { get; set; } + + [ProtoMember(6)] public PicUrlExtInfo PicUrlExtInfo { get; set; } + + [ProtoMember(7)] public VideoExtInfo VideoExtInfo { get; set; } +} + +[ProtoContract] +internal class IPv4 +{ + [ProtoMember(1)] public uint OutIP { get; set; } + + [ProtoMember(2)] public uint OutPort { get; set; } + + [ProtoMember(3)] public uint InIP { get; set; } + + [ProtoMember(4)] public uint InPort { get; set; } + + [ProtoMember(5)] public uint IPType { get; set; } +} + +[ProtoContract] +internal class IPv6 +{ + [ProtoMember(1)] public byte[] OutIP { get; set; } + + [ProtoMember(2)] public uint OutPort { get; set; } + + [ProtoMember(3)] public byte[] InIP { get; set; } + + [ProtoMember(4)] public uint InPort { get; set; } + + [ProtoMember(5)] public uint IPType { get; set; } +} + +[ProtoContract] +internal class UploadResp +{ + [ProtoMember(1)] public string? UKey { get; set; } + + [ProtoMember(2)] public uint UKeyTtlSecond { get; set; } + + [ProtoMember(3)] public List IPv4s { get; set; } + + [ProtoMember(4)] public List IPv6s { get; set; } + + [ProtoMember(5)] public ulong MsgSeq { get; set; } + + [ProtoMember(6)] public MsgInfo MsgInfo { get; set; } + + [ProtoMember(7)] public List Ext { get; set; } + + [ProtoMember(8)] public byte[] CompatQMsg { get; set; } + + [ProtoMember(10)] public List SubFileInfos { get; set; } +} + +[ProtoContract] +internal class RichMediaStorageTransInfo +{ + [ProtoMember(1)] public uint SubType { get; set; } + + [ProtoMember(2)] public uint ExtType { get; set; } + + [ProtoMember(3)] public byte[] ExtValue { get; set; } +} + +[ProtoContract] +internal class SubFileInfo +{ + [ProtoMember(1)] public uint SubType { get; set; } + + [ProtoMember(2)] public string? UKey { get; set; } + + [ProtoMember(3)] public uint UKeyTtlSecond { get; set; } + + [ProtoMember(4)] public List IPv4s { get; set; } + + [ProtoMember(5)] public List IPv6s { get; set; } +} + +[ProtoContract] +internal class DownloadSafeResp { } + +[ProtoContract] +internal class UploadKeyRenewalResp +{ + [ProtoMember(1)] public string Ukey { get; set; } + + [ProtoMember(2)] public ulong UkeyTtlSec { get; set; } +} + +[ProtoContract] +internal class MsgInfoAuthResp +{ + [ProtoMember(1)] public uint AuthCode { get; set; } + + [ProtoMember(2)] public byte[] Msg { get; set; } + + [ProtoMember(3)] public ulong ResultTime { get; set; } +} + +[ProtoContract] +internal class UploadCompletedResp +{ + [ProtoMember(1)] public ulong MsgSeq { get; set; } +} + +[ProtoContract] +internal class DeleteResp { } + +[ProtoContract] +internal class DownloadRKeyResp +{ + [ProtoMember(1)] public List RKeys { get; set; } +} + +[ProtoContract] +internal class RKeyInfo +{ + [ProtoMember(1)] public string Rkey { get; set; } + + [ProtoMember(2)] public ulong RkeyTtlSec { get; set; } + + [ProtoMember(3)] public uint StoreId { get; set; } + + [ProtoMember(4)] public uint? RkeyCreateTime { get; set; } + + [ProtoMember(5)] public uint? Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriend.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriend.cs new file mode 100644 index 0000000..d1fc9f4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriend.cs @@ -0,0 +1,16 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbFriend +{ + [ProtoMember(1)] public string Uid { get; set; } + + [ProtoMember(2)] public uint CustomGroup { get; set; } + + [ProtoMember(3)] public uint Uin { get; set; } + + [ProtoMember(10001)] public List Additional { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendAdditional.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendAdditional.cs new file mode 100644 index 0000000..7b3dd76 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendAdditional.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbFriendAdditional +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] public OidbFriendLayer1 Layer1 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendByteProperty.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendByteProperty.cs new file mode 100644 index 0000000..59287d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendByteProperty.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbFriendByteProperty +{ + [ProtoMember(1)] public uint Code { get; set; } + + [ProtoMember(2)] public byte[] Value { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendLayer1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendLayer1.cs new file mode 100644 index 0000000..9d64768 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendLayer1.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbFriendLayer1 +{ + [ProtoMember(2)] public List Properties { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendProperty.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendProperty.cs new file mode 100644 index 0000000..9b8e215 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbFriendProperty.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbFriendProperty +{ + [ProtoMember(1)] public uint Code { get; set; } + + [ProtoMember(2)] public string Value { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbLafter.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbLafter.cs new file mode 100644 index 0000000..c1d2023 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbLafter.cs @@ -0,0 +1,14 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbLafter +{ + [ProtoMember(1)] public int Type { get; set; } + + [ProtoMember(2)] public byte[] D2 { get; set; } + + [ProtoMember(3)] public uint SubAppid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbNumber.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbNumber.cs new file mode 100644 index 0000000..4827eeb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbNumber.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbNumber +{ + [ProtoMember(1)] public List Numbers { get; set; } = new(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbProperty.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbProperty.cs new file mode 100644 index 0000000..ca1f810 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbProperty.cs @@ -0,0 +1,12 @@ +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbProperty +{ + [ProtoMember(1)] public string Key { get; set; } + + [ProtoMember(2)] public byte[] Value { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbTwoNumber.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbTwoNumber.cs new file mode 100644 index 0000000..9ad551d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Generics/OidbTwoNumber.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbTwoNumber +{ + [ProtoMember(1)] public uint Number1 { get; set; } + + [ProtoMember(2)] public uint Number2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs new file mode 100644 index 0000000..dbad53a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Internal.Packets.Service.Oidb; + +[AttributeUsage(AttributeTargets.Class)] +internal class OidbSvcTrpcTcpAttribute : Attribute +{ + public uint Command { get; set; } + + public uint SubCommand { get; set; } + + public bool IsLafter { get; set; } + + public OidbSvcTrpcTcpAttribute(uint command, uint subCommand, bool isLafter = false) + { + Command = command; + SubCommand = subCommand; + IsLafter = isLafter; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs new file mode 100644 index 0000000..24155fc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs @@ -0,0 +1,76 @@ +using System.Reflection; +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb; + +/// +/// This class to declear a OidbSvcTrpcTcp packet 我愿称之为Protobuf版Tlv +/// Responsible for the uploading of NotOnlineFile, originally from OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700 of legacy oicq protocol +/// +[ProtoContract] +internal class OidbSvcTrpcTcpBase where T : class +{ + private static readonly Dictionary OidbReference; + + static OidbSvcTrpcTcpBase() + { + OidbReference = new Dictionary(); + + var assembly = Assembly.GetExecutingAssembly(); + var types = assembly.GetTypeByAttributes(out var attributes); + + for (int i = 0; i < types.Count; i++) + { + var type = types[i]; + var attribute = attributes[i]; + OidbReference[type] = (attribute.Command, attribute.SubCommand); + } + } + + [ProtoMember(1)] public uint Command { get; set; } + + [ProtoMember(2)] public uint SubCommand { get; set; } + + [ProtoMember(3)] public uint ErrorCode { get; set; } + + [ProtoMember(4)] public T Body { get; set; } + + [ProtoMember(5)] public string ErrorMsg { get; set; } + + [ProtoMember(7)] public OidbLafter? Lafter { get; set; } // if Lafter is null, it would not be serialized + + [ProtoMember(11)] public List Properties { get; set; } + + [ProtoMember(12)] public int Reserved { get; set; } + + public OidbSvcTrpcTcpBase() { } + + public OidbSvcTrpcTcpBase(T body, bool isLafter = false, bool isUid = false) + { + var (command, subCommand) = OidbReference[typeof(T)]; + + Command = command; + SubCommand = subCommand; + Body = body; + ErrorMsg = ""; + Reserved = Convert.ToInt32(isUid); + Lafter = isLafter ? new OidbLafter() : null; + Properties = new List(); + } + + public OidbSvcTrpcTcpBase(T body, uint command,uint subCommand, bool isLafter = false, bool isUid = false) + { + Command = command; + SubCommand = subCommand; + Body = body; + ErrorMsg = ""; + Reserved = Convert.ToInt32(isUid); + Lafter = isLafter ? new OidbLafter() : null; + Properties = new List(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0x90EE_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0x90EE_1.cs new file mode 100644 index 0000000..0ca6e73 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0x90EE_1.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x90EE, 1)] +internal class OidbSvcTrpcTcp0x90EE_1 +{ + [ProtoMember(1)] public uint FaceId { get; set; } + + [ProtoMember(2)] public uint TargetMsgSeq { get; set; } + + [ProtoMember(3)] public uint TargetMsgSeq_2 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } // group 2 friend 1 ? + + [ProtoMember(5)] public uint? TargetGroupId { get; set; } + + [ProtoMember(6)] public string? TargetUid { get; set; } + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs new file mode 100644 index 0000000..4fc299c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs @@ -0,0 +1,41 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0xE07, 0)] +internal class OidbSvcTrpcTcp0xE07_0 +{ + [ProtoMember(1)] public uint Version { get; set; } + + [ProtoMember(2)] public uint Client { get; set; } + + [ProtoMember(3)] public uint Entrance { get; set; } + + [ProtoMember(10)] public OcrReqBody OcrReqBody { get; set; } +} + +[ProtoContract] +internal class OcrReqBody +{ + [ProtoMember(1)] public string ImageUrl { get; set; } + + [ProtoMember(2)] public uint LanguageType { get; set; } + + [ProtoMember(3)] public uint Scene { get; set; } + + [ProtoMember(10)] public string OriginMd5 { get; set; } + + [ProtoMember(11)] public string AfterCompressMd5 { get; set; } + + [ProtoMember(12)] public string AfterCompressFileSize { get; set; } + + [ProtoMember(13)] public string AfterCompressWeight { get; set; } + + [ProtoMember(14)] public string AfterCompressHeight { get; set; } + + [ProtoMember(15)] public bool IsCut { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_0.cs new file mode 100644 index 0000000..92594fa --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_0.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Get Cookie +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x102A, 0)] +internal class OidbSvcTrpcTcp0x102A_0 +{ + [ProtoMember(1)] public List Domain { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_1.cs new file mode 100644 index 0000000..b41ac2a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x102A_1.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +/// +/// Fetch Client Key: 我也觉得抽象 可是他真的是空的 +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x102A, 1)] +internal class OidbSvcTrpcTcp0x102A_1 +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1096_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1096_1.cs new file mode 100644 index 0000000..7286045 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1096_1.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +[OidbSvcTrpcTcp(0x1096, 1)] +internal class OidbSvcTrpcTcp0x1096_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public string Uid { get; set; } + + [ProtoMember(3)] public bool IsAdmin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1097_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1097_1.cs new file mode 100644 index 0000000..5e8bce9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1097_1.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +/// +/// Quit Group +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x1097, 1)] +internal class OidbSvcTrpcTcp0x1097_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_1.cs new file mode 100644 index 0000000..bfb7456 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_1.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +/// +/// Fetch Friends & Group Notification List +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x10c0, 1)] +internal class OidbSvcTrpcTcp0x10C0_1 +{ + [ProtoMember(1)] public uint Count { get; set; } // 20 + + [ProtoMember(2)] public uint Field2 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_2.cs new file mode 100644 index 0000000..20e5afe --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C0_2.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +/// +/// Fetch Friends & Group Notification filtered List +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x10c0, 2)] +internal class OidbSvcTrpcTcp0x10C0_2 +{ + [ProtoMember(1)] public uint Count { get; set; } // 20 + + [ProtoMember(2)] public uint Field2 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C8.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C8.cs new file mode 100644 index 0000000..9f4869d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x10C8.cs @@ -0,0 +1,28 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +/// +/// Accept group request +/// +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C8 +{ + [ProtoMember(1)] public uint Accept { get; set; } // 2 for reject, 1 for accept, 3 for ignore + + [ProtoMember(2)] public OidbSvcTrpcTcp0x10C8Body? Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C8Body +{ + [ProtoMember(1)] public ulong Sequence { get; set; } // 1 + + [ProtoMember(2)] public uint EventType { get; set; } // 2 + + [ProtoMember(3)] public uint GroupUin { get; set; } // 3 + + [ProtoMember(4)] public string? Message { get; set; } // "" +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x112E_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x112E_1.cs new file mode 100644 index 0000000..b51ceaa --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x112E_1.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +[OidbSvcTrpcTcp(0x112E, 1)] +internal class OidbSvcTrpcTcp0x112E_1 +{ + [ProtoMember(3)] public uint BotId { get; set; } + + [ProtoMember(4)] public uint Seq { get; set; } = 111111; + + [ProtoMember(5)] public string B_id { get; set; } = ""; + + [ProtoMember(6)] public string B_data { get; set; } = ""; + + [ProtoMember(7)] public uint IDD { get; set; } = 0; + + [ProtoMember(8)] public uint GroupUin { get; set; } + + [ProtoMember(9)] public uint GroupType { get; set; } = 1; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1253_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1253_1.cs new file mode 100644 index 0000000..f680a4d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1253_1.cs @@ -0,0 +1,28 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group Member Mute +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x1253, 1)] +internal class OidbSvcTrpcTcp0x1253_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Type { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x1253_1Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x1253_1Body +{ + [ProtoMember(1)] public string TargetUid { get; set; } + + [ProtoMember(2)] public uint Duration { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x126B_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x126B_0.cs new file mode 100644 index 0000000..f39e753 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x126B_0.cs @@ -0,0 +1,45 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +[OidbSvcTrpcTcp(0x126B, 0)] +internal class OidbSvcTrpcTcp0x126B_0 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x126B_0_Field1 Field1 { get; set; } = new(); +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x126B_0_Field1 +{ + [ProtoMember(1)] public string? TargetUid { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x126B_0_Field1_2 Field2 { get; set; } = new(); + + [ProtoMember(3)] public bool Block { get; set; } + + [ProtoMember(4)] public bool Field4 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x126B_0_Field1_2 +{ + [ProtoMember(1)] public uint Field1 { get; set; } = 130; + + [ProtoMember(2)] public uint Field2 { get; set; } = 109; + + [ProtoMember(3)] public OidbSvcTrpcTcp0x126B_0_Field1_2_3 Field3 { get; set; } = new(); +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x126B_0_Field1_2_3 +{ + [ProtoMember(1)] public uint Field1 { get; set; } = 8; + + [ProtoMember(2)] public uint Field2 { get; set; } = 8; + + [ProtoMember(3)] public uint Field3 { get; set; } = 50; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1277_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1277_0.cs new file mode 100644 index 0000000..c52fd20 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x1277_0.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0x1277, 0)] +internal class OidbSvcTrpcTcp0x1277_0 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x1277_0Body Body { get; set; } +} + + +[ProtoContract] +internal class OidbSvcTrpcTcp0x1277_0Body +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x1277_0Device Device { get; set; } + + [ProtoMember(2)] public bool GuidEncryptedType { get; set; } + + [ProtoMember(3)] public bool AutoLogin { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x1277_0Device +{ + [ProtoMember(1)] public byte[] Guid { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } + + [ProtoMember(3)] public string PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5CF_11.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5CF_11.cs new file mode 100644 index 0000000..ea31328 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5CF_11.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x5CF, 11)] +internal class OidbSvcTrpcTcp0x5CF_11 +{ + [ProtoMember(1)] public int Field1 { get; set; } // 1 + + [ProtoMember(3)] public int Field3 { get; set; } // 6 + + [ProtoMember(4)] public string SelfUid { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } // 0 + + [ProtoMember(6)] public int Field6 { get; set; } // 80 + + [ProtoMember(8)] public int Field8 { get; set; } // 2 + + [ProtoMember(9)] public int Field9 { get; set; } // 0 + + [ProtoMember(12)] public int Field12 { get; set; } // 1 + + [ProtoMember(22)] public int Field22 { get; set; } // 1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_1.cs new file mode 100644 index 0000000..2d3f596 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_1.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x5d6, 1)] +internal class OidbSvcTrpcTcp0x5D6_1 +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x5D6_1Info Info { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5D6_1Info +{ + [ProtoMember(2)] public uint GroupUin { get; set; } + + [ProtoMember(400)] public OidbSvcTrpcTcp0x5D6_1Field4_2_400 Field400 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5D6_1Field4_2_400 +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public byte[] Timestamp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_18.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_18.cs new file mode 100644 index 0000000..7140119 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x5D6_18.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x5d6, 18)] +internal class OidbSvcTrpcTcp0x5D6_18 +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x5D6_18Info Info { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5D6_18Info +{ + [ProtoMember(1)] public string FriendUid { get; set; } + + [ProtoMember(400)] public OidbSvcTrpcTcp0x5D6_18Field4_2_400 Field400 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5D6_18Field4_2_400 +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public byte[] Timestamp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D6.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D6.cs new file mode 100644 index 0000000..79045d0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D6.cs @@ -0,0 +1,88 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group File Upload +/// +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x6D6Upload? File { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D6Download? Download { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x6D6Delete? Delete { get; set; } + + [ProtoMember(6)] public OidbSvcTrpcTcp0x6D6Move? Move { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6Upload +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 + + [ProtoMember(3)] public uint BusId { get; set; } // 102 + + [ProtoMember(4)] public uint Entrance { get; set; } // 6 + + [ProtoMember(5)] public string TargetDirectory { get; set; } + + [ProtoMember(6)] public string FileName { get; set; } + + [ProtoMember(7)] public string LocalDirectory { get; set; } + + [ProtoMember(8)] public long FileSize { get; set; } + + [ProtoMember(9)] public byte[] FileSha1 { get; set; } + + [ProtoMember(10)] public byte[] FileSha3 { get; set; } // ? + + [ProtoMember(11)] public byte[] FileMd5 { get; set; } + + [ProtoMember(15)] public bool Field15 { get; set; } // true +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6Download +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 + + [ProtoMember(3)] public uint BusId { get; set; } // 102 + + [ProtoMember(4)] public string FileId { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6Delete +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public uint BusId { get; set; } // 102 + + [ProtoMember(5)] public string FileId { get; set; } +} + + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6Move +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 + + [ProtoMember(3)] public uint BusId { get; set; } // 102 + + [ProtoMember(4)] public string fileId { get; set; } + + [ProtoMember(5)] public string ParentDirectory { get; set; } + + [ProtoMember(6)] public string TargetDirectory { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_0.cs new file mode 100644 index 0000000..d138e34 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_0.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Create Folder +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x6D7, 0)] +internal class OidbSvcTrpcTcp0x6D7_0 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x6D7_0Create Create { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_0Create +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public string RootDirectory { get; set; } + + [ProtoMember(4)] public string FolderName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_1.cs new file mode 100644 index 0000000..b518d60 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_1.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Delete Folder +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x6D7, 1)] +internal class OidbSvcTrpcTcp0x6D7_1 +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0x6D7_1Delete Delete { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_1Delete +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public string FolderId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_2.cs new file mode 100644 index 0000000..5ee84ac --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D7_2.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Delete Folder +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x6D7, 2)] +internal class OidbSvcTrpcTcp0x6D7_2 +{ + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D7_2Rename Rename { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_2Rename +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public string FolderId { get; set; } + + [ProtoMember(4)] public string NewFolderName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D8.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D8.cs new file mode 100644 index 0000000..45335dd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D8.cs @@ -0,0 +1,55 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8 +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0x6D8List? List { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D8Count? Count { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x6D8Space? Space { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8List +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 + + [ProtoMember(3)] public string TargetDirectory { get; set; } // / + + [ProtoMember(5)] public uint FileCount { get; set; } // 20 + + [ProtoMember(9)] public uint SortBy { get; set; } // 1 + + [ProtoMember(13)] public uint StartIndex { get; set; } // default 0 + + [ProtoMember(17)] public uint Field17 { get; set; } // 2 + + [ProtoMember(18)] public uint Field18 { get; set; } // 0 +} + + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8Count +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 + + [ProtoMember(3)] public uint BusId { get; set; } // 6 +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8Space +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint AppId { get; set; } // 7 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D9_4.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D9_4.cs new file mode 100644 index 0000000..87369ee --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x6D9_4.cs @@ -0,0 +1,40 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group Send File +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x6D9, 4)] +internal class OidbSvcTrpcTcp0x6D9_4 +{ + [ProtoMember(5)] public OidbSvcTrpcTcp0x6D9_4Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D9_4Body +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Type { get; set; } // 2 + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D9_4Info Info { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D9_4Info +{ + [ProtoMember(1)] public uint BusiType { get; set; } // 102 + + [ProtoMember(2)] public string FileId { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } // random + + [ProtoMember(4)] public string? Field4 { get; set; } // null + + [ProtoMember(5)] public bool Field5 { get; set; } // true +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x758_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x758_1.cs new file mode 100644 index 0000000..c3a27f9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x758_1.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// Resharper disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0x758, 1)] +internal class OidbSvcTrpcTcp0x758_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public List? UidList { get; set; } + + [ProtoMember(10)] public uint? Field10 { get; set; } // 0 +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x758_1Uid +{ + [ProtoMember(1)] public string? InviteUid { get; set; } + + [ProtoMember(2)] public uint? SourceGroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C1_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C1_1.cs new file mode 100644 index 0000000..7b2d664 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C1_1.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0x7c1, 1)] +internal class OidbSvcTrpcTcp0x7C1_1 +{ + [ProtoMember(1)] public uint Field1 { get; set; } // 1 + + [ProtoMember(2)] public uint SelfUin { get; set; } + + [ProtoMember(3)] public uint TargetUin { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } // 3999 + + [ProtoMember(5)] public uint Field5 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C2_5.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C2_5.cs new file mode 100644 index 0000000..a35f4d3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7C2_5.cs @@ -0,0 +1,36 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0x7c2, 5)] +internal class OidbSvcTrpcTcp0x7C2_5 +{ + [ProtoMember(1)] public uint SelfUin { get; set; } + + [ProtoMember(2)] public uint TargetUin { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } // 1 + + [ProtoMember(4)] public uint Field4 { get; set; } // 1 + + [ProtoMember(5)] public uint Field5 { get; set; } // 0 + + [ProtoMember(7)] public string Remark { get; set; } = ""; + + [ProtoMember(11)] public uint SourceId { get; set; } // 1 + + [ProtoMember(12)] public uint SubSourceId { get; set; } // 3 + + [ProtoMember(18)] public string Verify { get; set; } = ""; + + [ProtoMember(20)] public uint CategoryId { get; set; } + + [ProtoMember(26)] public string Answer { get; set; } = ""; + + [ProtoMember(28)] public uint Field28 { get; set; } // 1 + + [ProtoMember(29)] public uint Field29 { get; set; } // 1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7E5_104.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7E5_104.cs new file mode 100644 index 0000000..2c7d928 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x7E5_104.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// Resharper Disable InconsistentNaming + +/// +/// Friend Likes +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x7e5, 104)] +internal class OidbSvcTrpcTcp0x7E5_104 +{ + [ProtoMember(11)] public string? TargetUid { get; set; } + + [ProtoMember(12)] public uint Field2 { get; set; } // 71 + + [ProtoMember(13)] public uint Field3 { get; set; } // 1 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x88D_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x88D_0.cs new file mode 100644 index 0000000..94a26b9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x88D_0.cs @@ -0,0 +1,73 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Get Cookie +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x88D, 0)] +internal class OidbSvcTrpcTcp0x88D_0 +{ + [ProtoMember(1)] + public uint Field1 { get; set; } + + [ProtoMember(2)] + public OidbSvcTrpcTcp0x88D_0Config Config { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x88D_0Config +{ + [ProtoMember(1)] + public ulong Uin { get; set; } + + [ProtoMember(2)] + public OidbSvcTrpcTcp0x88D_0Flags Flags { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x88D_0Flags +{ + [ProtoMember(1)] + public bool? OwnerUid { get; set; } + + [ProtoMember(2)] + public bool? CreateTime { get; set; } + + [ProtoMember(5)] + public bool? MaxMemberCount { get; set; } + + [ProtoMember(6)] + public bool? MemberCount { get; set; } + + [ProtoMember(10)] + public bool? Level { get; set; } + + [ProtoMember(15)] + public string? Name { get; set; } + + [ProtoMember(16)] + public string? NoticePreview { get; set; } + + [ProtoMember(21)] + public bool? Uin { get; set; } + + [ProtoMember(22)] + public bool? LastSequence { get; set; } + + [ProtoMember(23)] + public bool? LastMessageTime { get; set; } + + [ProtoMember(24)] + public bool? Question { get; set; } + + [ProtoMember(25)] + public string? Answer { get; set; } + + [ProtoMember(29)] + public string? MaxAdminCount { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_0.cs new file mode 100644 index 0000000..fc3ea07 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_0.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +/// +/// Group Global Mute +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x89A, 0)] +internal class OidbSvcTrpcTcp0x89A_0 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x89A_0State State { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x89A_0State +{ + [ProtoMember(17)] public uint S { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_15.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_15.cs new file mode 100644 index 0000000..7f34916 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89A_15.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Rename Group Title +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x89A, 15)] +internal class OidbSvcTrpcTcp0x89A_15 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x89A_15Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x89A_15Body +{ + [ProtoMember(3)] public string TargetName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89E_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89E_0.cs new file mode 100644 index 0000000..5f92593 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x89E_0.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// Resharper disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0x89e, 0)] +internal class OidbSvcTrpcTcp0x89E_0 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public string SourceUid { get; set; } + + [ProtoMember(3)] public string TargetUid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A0_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A0_1.cs new file mode 100644 index 0000000..cc944fd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A0_1.cs @@ -0,0 +1,22 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group Kick Member +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x8A0, 1)] +internal class OidbSvcTrpcTcp0x8A0_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public string TargetUid { get; set; } + + [ProtoMember(4)] public bool RejectAddRequest { get; set; } + + [ProtoMember(5)] public string Reason { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A7_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A7_0.cs new file mode 100644 index 0000000..a3787f9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8A7_0.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x8A7, 0)] +internal class OidbSvcTrpcTcp0x8A7_0 +{ + [ProtoMember(1)] public uint SubCmd { get; set; } + + [ProtoMember(2)] public uint LimitIntervalTypeForUin { get; set; } + + [ProtoMember(3)] public uint LimitIntervalTypeForGroup { get; set; } + + [ProtoMember(4)] public uint Uin { get; set; } + + [ProtoMember(5)] public uint GroupCode { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8FC.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8FC.cs new file mode 100644 index 0000000..e3ea2f1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x8FC.cs @@ -0,0 +1,31 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Rename Group Member +/// +[ProtoContract] +internal class OidbSvcTrpcTcp0x8FC +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x8FCBody Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x8FCBody +{ + [ProtoMember(1)] public string TargetUid { get; set; } + + [ProtoMember(5)] public string SpecialTitle { get; set; } + + [ProtoMember(6)] public int SpecialTitleExpireTime { get; set; } + + [ProtoMember(7)] public string UinName { get; set; } + + [ProtoMember(8)] public string TargetName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x907D_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x907D_1.cs new file mode 100644 index 0000000..83cac2b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x907D_1.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +[OidbSvcTrpcTcp(0x907D, 1)] +internal class OidbSvcTrpcTcp0x907D_1 +{ + [ProtoMember(1)] public uint BotId { get; set; } + + [ProtoMember(2)] public uint type { get; set; } = 2; + + [ProtoMember(3)] public uint On { get; set; } = 1; + + [ProtoMember(4)] public uint GroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9082.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9082.cs new file mode 100644 index 0000000..3ea7091 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9082.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// Resharper Disable InconsistentNaming + +/// +/// Group add and reduce Reaction +/// +[ProtoContract] +internal class OidbSvcTrpcTcp0x9082 +{ + [ProtoMember(2)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public uint Sequence { get; set; } + + [ProtoMember(4)] public string? Code { get; set; } + + [ProtoMember(5)] public uint Type { get; set; } // 1 Face 2 Emoji + + [ProtoMember(6)] public bool Field6 { get; set; } // false + + [ProtoMember(7)] public bool Field7 { get; set; } // false +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9154_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9154_1.cs new file mode 100644 index 0000000..09c246d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x9154_1.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x9154, 1)] +internal class OidbSvcTrpcTcp0x9154_1 +{ + [ProtoMember(1)] public int Field1 { get; set; } // 0 + + [ProtoMember(2)] public int Field2 { get; set; } // 7 + + [ProtoMember(3)] public string Field3 { get; set; } // 0 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs new file mode 100644 index 0000000..de0b329 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +[ProtoContract] +[OidbSvcTrpcTcp(0x929B, 0)] +internal class OidbSvcTrpcTcp0x929B_0 +{ + [ProtoMember(1)] public uint GroupCode { get; set; } + + [ProtoMember(2)] public string VoiceId { get; set; } + + [ProtoMember(3)] public string Text { get; set; } + + [ProtoMember(4)] public uint ChatType { get; set; } = 1; //1 voice,2 song + + [ProtoMember(5)] public OidbSvcTrpcTcp0x929B_0ClientMsgInfo ClientMsgInfo { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929B_0ClientMsgInfo +{ + [ProtoMember(1)] public uint MsgRandom { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs new file mode 100644 index 0000000..c11af0d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x929C, 0)] +internal class OidbSvcTrpcTcp0x929C_0 +{ + [ProtoMember(1)] public uint Group { get; set; } + + [ProtoMember(2)] public uint ChatType { get; set; } = 1; //1 voice,2 song +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs new file mode 100644 index 0000000..3f01257 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x929D, 0)] +internal class OidbSvcTrpcTcp0x929D_0 +{ + [ProtoMember(1)] public uint Group { get; set; } + + [ProtoMember(2)] public uint ChatType { get; set; } = 1; //1 voice,2 song +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x972_6.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x972_6.cs new file mode 100644 index 0000000..08d67e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x972_6.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +[OidbSvcTrpcTcp(0x972, 6)] +internal class OidbSvcTrpcTcp0x972_6 +{ + [ProtoMember(1)] public string TargetUin { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x972_6Settings Settings { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x972_6Settings +{ + [ProtoMember(4)] public uint Field4 { get; set; } // 25 + + [ProtoMember(11)] public string Field11 { get; set; } // "" + + [ProtoMember(55)] public string Setting { get; set; } // {"search_by_uid":true, "scenario":"related_people_and_groups_panel"} +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xB5D_44.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xB5D_44.cs new file mode 100644 index 0000000..04b17ca --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xB5D_44.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Set Friend Request +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xB5D, 44)] +public class OidbSvcTrpcTcp0xB5D_44 +{ + [ProtoMember(1)] public uint Accept { get; set; } // 3 for accept, 5 for reject + + [ProtoMember(2)] public string TargetUid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1200.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1200.cs new file mode 100644 index 0000000..de9f2c9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1200.cs @@ -0,0 +1,38 @@ +using ProtoBuf; +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0xE37, 1200)] +internal class OidbSvcTrpcTcp0xE37_1200 +{ + [ProtoMember(1)] public uint SubCommand { get; set; } = 1200; + + [ProtoMember(2)] public int Field2 { get; set; } = 1; // Unknown + + [ProtoMember(14)] public OidbSvcTrpcTcp0xE37_1200Body Body { get; set; } + + [ProtoMember(101)] public int Field101 { get; set; } = 3; // Unknown + + [ProtoMember(102)] public int Field102 { get; set; } = 103; // Unknown + + [ProtoMember(200)] public int Field200 { get; set; } = 1; // Unknown + + [ProtoMember(99999)] public byte[] Field99999 { get; set; } = { 0xc0, 0x85, 0x2c, 0x01 }; // Actually it is a sub-proto 90200: 1, but we it would be more easy to just hardcode it +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1200Body +{ + [ProtoMember(10)] public string ReceiverUid { get; set; } + + [ProtoMember(20)] public string FileUuid { get; set; } + + [ProtoMember(30)] public int Type { get; set; } = 2; + + [ProtoMember(60)] public string FileHash { get; set; } + + [ProtoMember(601)] public int T2 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1700.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1700.cs new file mode 100644 index 0000000..b115125 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_1700.cs @@ -0,0 +1,47 @@ +using ProtoBuf; +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +/// +/// Upload Offline File +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xE37, 1700)] +internal class OidbSvcTrpcTcp0xE37_1700 +{ + [ProtoMember(1)] public uint Command { get; set; } // 1700 + + [ProtoMember(2)] public int Seq { get; set; } // 0 + + [ProtoMember(19)] public ApplyUploadReqV3 Upload { get; set; } + + [ProtoMember(101)] public int BusinessId { get; set; } // 3 + + [ProtoMember(102)] public int ClientType { get; set; } // 1 + + [ProtoMember(200)] public int FlagSupportMediaPlatform { get; set; } // 1 +} + +[ProtoContract] +internal class ApplyUploadReqV3 +{ + [ProtoMember(10)] public string SenderUid { get; set; } + + [ProtoMember(20)] public string ReceiverUid { get; set; } + + [ProtoMember(30)] public uint FileSize { get; set; } + + [ProtoMember(40)] public string FileName { get; set; } + + [ProtoMember(50)] public byte[] Md510MCheckSum { get; set; } + + [ProtoMember(60)] public byte[] Sha1CheckSum { get; set; } + + [ProtoMember(70)] public string LocalPath { get; set; } + + [ProtoMember(110)] public byte[] Md5CheckSum { get; set; } + + [ProtoMember(120)] public byte[] Sha3CheckSum { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_800.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_800.cs new file mode 100644 index 0000000..849fbe5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xE37_800.cs @@ -0,0 +1,37 @@ +using ProtoBuf; +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +/// +/// Get Offline File Download +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xE37, 800)] +internal class OidbSvcTrpcTcp0xE37_800 +{ + [ProtoMember(1)] public uint SubCommand => 800; + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown + + [ProtoMember(10)] public OidbSvcTrpcTcp0xE37_800Body Body { get; set; } + + [ProtoMember(101)] public int Field101 { get; set; } // Unknown + + [ProtoMember(102)] public int Field102 { get; set; } // Unknown + + [ProtoMember(200)] public int Field200 { get; set; } // Unknown +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_800Body +{ + [ProtoMember(10)] public string SenderUid { get; set; } + + [ProtoMember(20)] public string ReceiverUid { get; set; } + + [ProtoMember(30)] public string FileUuid { get; set; } + + [ProtoMember(40)] public string FileHash { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEAC.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEAC.cs new file mode 100644 index 0000000..ee81a17 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEAC.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +internal class OidbSvcTrpcTcp0xEAC +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Sequence { get; set; } + + [ProtoMember(3)] public uint Random { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEB7_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEB7_1.cs new file mode 100644 index 0000000..007bc21 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xEB7_1.cs @@ -0,0 +1,27 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group Clock In +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xEB7, 1)] +internal class OidbSvcTrpcTcp0xEB7_1 +{ + [ProtoMember(2)] public BodyClass Body { get; set; } + + [ProtoContract] + internal class BodyClass + { + [ProtoMember(1)] public string Uin { get; set; } + + [ProtoMember(2)] public string GroupUin { get; set; } + + // 不确定要不要加,测试过没有这个参数也是可以的 + [ProtoMember(3)] public string AppVersion { get; set; } + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xED3_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xED3_1.cs new file mode 100644 index 0000000..9dfdad4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xED3_1.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming + +[OidbSvcTrpcTcp(0xed3, 1)] +[ProtoContract] +internal class OidbSvcTrpcTcp0xED3_1 +{ + [ProtoMember(1)] public uint Uin { get; set; } + + [ProtoMember(2)] public uint GroupUin { get; set; } // same when poke type is friend + + [ProtoMember(5)] public uint FriendUin { get; set; } + + [ProtoMember(6)] public uint? Ext { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF16_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF16_1.cs new file mode 100644 index 0000000..e086920 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF16_1.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Group Remark +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xF16, 1)] +internal class OidbSvcTrpcTcp0xF16_1 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xF16_1Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xF16_1Body +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(3)] public string TargetRemark { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF8E_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF8E_1.cs new file mode 100644 index 0000000..724e9d1 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF8E_1.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xF8E_1 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF90.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF90.cs new file mode 100644 index 0000000..e22e635 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xF90.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xF90 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Sequence { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFD4_1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFD4_1.cs new file mode 100644 index 0000000..6134b99 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFD4_1.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Fetch Friends List +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xfd4, 1)] +internal class OidbSvcTrpcTcp0xFD4_1 +{ + [ProtoMember(2)] public uint FriendCount { get; set; } = 300; // paging get num + + [ProtoMember(4)] public uint Field4 { get; set; } = 0; + + [ProtoMember(5)] public OidbSvcTrpcTcp0xFD4_1Uin NextUin { get; set; } + + [ProtoMember(6)] public uint Field6 { get; set; } = 1; + + [ProtoMember(7)] public uint Field7 { get; set; } = int.MaxValue; + + [ProtoMember(10001)] public List Body { get; set; } + + [ProtoMember(10002)] public List Field10002 { get; set; } = new() { 13578, 13579, 13573, 13572, 13568 }; + + [ProtoMember(10003)] public uint Field10003 { get; set; } = 4051; +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFD4_1Body +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] public OidbNumber Number { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFD4_1Uin +{ + [ProtoMember(1)] public uint Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE1_2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE1_2.cs new file mode 100644 index 0000000..5cfbb47 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE1_2.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +// Resharper Disable InconsistentNaming + +[ProtoContract] +[OidbSvcTrpcTcp(0xfe1, 2)] +internal class OidbSvcTrpcTcp0xFE1_2Uid +{ + [ProtoMember(1)] public string? Uid { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public List? Keys { get; set; } // can be regarded as constants +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE1_2Uin +{ + [ProtoMember(1)] public uint Uin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public List? Keys { get; set; } // can be regarded as constants +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE1_2Key +{ + [ProtoMember(1)] public uint Key { get; set; } // 傻逼 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE5_2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE5_2.cs new file mode 100644 index 0000000..518cfd2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE5_2.cs @@ -0,0 +1,120 @@ +// Resharper disable InconsistentNaming + +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0xFE5, 2)] +internal class OidbSvcTrpcTcp0xFE5_2 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xFE5_2Config Config { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Config +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xFE5_2Config1 Config1 { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0xFE5_2Config2 Config2 { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0xFE5_2Config3 Config3 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Config1 +{ + [ProtoMember(1)] public bool GroupOwner { get; set; } = true; + + [ProtoMember(2)] public bool Field2 { get; set; } = true; + + [ProtoMember(3)] public bool MemberMax { get; set; } = true; + + [ProtoMember(4)] public bool MemberCount { get; set; } = true; + + [ProtoMember(5)] public bool GroupName { get; set; } = true; + + [ProtoMember(8)] public bool Field8 { get; set; } = true; + + [ProtoMember(9)] public bool Field9 { get; set; } = true; + + [ProtoMember(10)] public bool Field10 { get; set; } = true; + + [ProtoMember(11)] public bool Field11 { get; set; } = true; + + [ProtoMember(12)] public bool Field12 { get; set; } = true; + + [ProtoMember(13)] public bool Field13 { get; set; } = true; + + [ProtoMember(14)] public bool Field14 { get; set; } = true; + + [ProtoMember(15)] public bool Field15 { get; set; } = true; + + [ProtoMember(16)] public bool Field16 { get; set; } = true; + + [ProtoMember(17)] public bool Field17 { get; set; } = true; + + [ProtoMember(18)] public bool Field18 { get; set; } = true; + + [ProtoMember(19)] public bool Question { get; set; } = true; + + [ProtoMember(20)] public bool Field20 { get; set; } = true; + + [ProtoMember(22)] public bool Field22 { get; set; } = true; + + [ProtoMember(23)] public bool Field23 { get; set; } = true; + + [ProtoMember(24)] public bool Field24 { get; set; } = true; + + [ProtoMember(25)] public bool Field25 { get; set; } = true; + + [ProtoMember(26)] public bool Field26 { get; set; } = true; + + [ProtoMember(27)] public bool Field27 { get; set; } = true; + + [ProtoMember(28)] public bool Field28 { get; set; } = true; + + [ProtoMember(29)] public bool Field29 { get; set; } = true; + + [ProtoMember(30)] public bool Field30 { get; set; } = true; + + [ProtoMember(31)] public bool Field31 { get; set; } = true; + + [ProtoMember(32)] public bool Field32 { get; set; } = true; + + [ProtoMember(5001)] public bool Field5001 { get; set; } = true; + + [ProtoMember(5002)] public bool Field5002 { get; set; } = true; + + [ProtoMember(5003)] public bool Field5003 { get; set; } = true; +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Config2 +{ + [ProtoMember(1)] public bool Field1 { get; set; } = true; + + [ProtoMember(2)] public bool Field2 { get; set; } = true; + + [ProtoMember(3)] public bool Field3 { get; set; } = true; + + [ProtoMember(4)] public bool Field4 { get; set; } = true; + + [ProtoMember(5)] public bool Field5 { get; set; } = true; + + [ProtoMember(6)] public bool Field6 { get; set; } = true; + + [ProtoMember(7)] public bool Field7 { get; set; } = true; + + [ProtoMember(8)] public bool Field8 { get; set; } = true; +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Config3 +{ + [ProtoMember(5)] public bool Field5 { get; set; } = true; + + [ProtoMember(6)] public bool Field6 { get; set; } = true; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE7_3.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE7_3.cs new file mode 100644 index 0000000..bd68159 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0xFE7_3.cs @@ -0,0 +1,66 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +/// +/// Fetch Group Member List +/// +[ProtoContract] +[OidbSvcTrpcTcp(0xfe7, 3)] +internal class OidbSvcTrpcTcp0xFE7_3 +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } // 5 + + [ProtoMember(3)] public uint Field3 { get; set; } // 2 + + [ProtoMember(4)] public OidbSvcTrpcScp0xFE7_3Body Body { get; set; } + + [ProtoMember(15)] public string? Token { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcScp0xFE7_3Body +{ + [ProtoMember(10)] public bool MemberName { get; set; } = true; // 1 + + [ProtoMember(11)] public bool MemberCard { get; set; } = true; // 1 + + [ProtoMember(12)] public bool Level { get; set; } = true; // 1 + + [ProtoMember(13)] public bool Field13 { get; set; } = true; // 1 + + [ProtoMember(16)] public bool Field16 { get; set; } = true; // 1 + + [ProtoMember(17)] public bool SpecialTitle { get; set; } = true; // 1 + + [ProtoMember(18)] public bool Field18 { get; set; } = true; // 1 + + [ProtoMember(20)] public bool Field20 { get; set; } = true; // 1 + + [ProtoMember(21)] public bool Field21 { get; set; } = true; // 1 + + [ProtoMember(100)] public bool JoinTimestamp { get; set; } = true; // 1 + + [ProtoMember(101)] public bool LastMsgTimestamp { get; set; } = true; // 1 + + [ProtoMember(102)] public bool ShutUpTimestamp { get; set; } = true; // 1 + + [ProtoMember(103)] public bool Field103 { get; set; } = true; // 1 + + [ProtoMember(104)] public bool Field104 { get; set; } = true; // 1 + + [ProtoMember(105)] public bool Field105 { get; set; } = true; // 1 + + [ProtoMember(106)] public bool Field106 { get; set; } = true; // 1 + + [ProtoMember(107)] public bool Permission { get; set; } = true; // 1 + + [ProtoMember(200)] public bool Field200 { get; set; } = true; + + [ProtoMember(201)] public bool Field201 { get; set; } = true; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_0Response.cs new file mode 100644 index 0000000..637c3bc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_0Response.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0x102A_0Response +{ + [ProtoMember(1)] public List Urls { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_1Response.cs new file mode 100644 index 0000000..51e0098 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x102A_1Response.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0x102A_1Response +{ + [ProtoMember(2)] public uint Field1 { get; set; } + + [ProtoMember(3)] public string ClientKey { get; set; } + + [ProtoMember(4)] public uint Expiration { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1096_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1096_1Response.cs new file mode 100644 index 0000000..6c4e611 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1096_1Response.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x1096_1Response +{ + [ProtoMember(1)] public string? Success { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x10C0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x10C0Response.cs new file mode 100644 index 0000000..da2e754 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x10C0Response.cs @@ -0,0 +1,60 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// Resharper Disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C0Response +{ + [ProtoMember(1)] public List? Requests { get; set; } + + [ProtoMember(2)] public ulong Field2 { get; set; } + + [ProtoMember(3)] public ulong NewLatestSeq { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(5)] public ulong Field5 { get; set; } + + [ProtoMember(6)] public uint Field6 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C0ResponseRequests +{ + [ProtoMember(1)] public ulong Sequence { get; set; } + + [ProtoMember(2)] public uint EventType { get; set; } // 13 for exit group, 22 for group request + + [ProtoMember(3)] public uint State { get; set; } // 2 for Join, 1 for waiting for action + + [ProtoMember(4)] public OidbSvcTrpcTcp0x10C0ResponseGroup Group { get; set; } + + [ProtoMember(5)] public OidbSvcTrpcTcp0x10C0ResponseUser Target { get; set; } + + [ProtoMember(6)] public OidbSvcTrpcTcp0x10C0ResponseUser? Invitor { get; set; } + + [ProtoMember(7)] public OidbSvcTrpcTcp0x10C0ResponseUser? Operator { get; set; } + + [ProtoMember(9)] public string Field9 { get; set; } + + [ProtoMember(10)] public string Comment { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C0ResponseGroup +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public string GroupName { get; set; } // 13 for exit group, 22 for group request +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x10C0ResponseUser +{ + [ProtoMember(1)] public string Uid { get; set; } + + [ProtoMember(2)] public string Name { get; set; } // 13 for exit group, 22 for group request +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1253_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1253_1Response.cs new file mode 100644 index 0000000..c33fa25 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x1253_1Response.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x1253_1Response +{ + [ProtoMember(2)] public string? Success { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x12B3_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x12B3_0Response.cs new file mode 100644 index 0000000..92b312c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x12B3_0Response.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x12B3_0Response +{ + [ProtoMember(1)] public List? Friends { get; set; } + + [ProtoMember(3)] public List? Groups { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x12B3_0ResponseFriend +{ + [ProtoMember(1)] public string Uid { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x12B3_0ResponseGroup +{ + [ProtoMember(1)] public uint Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x5CF_11Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x5CF_11Response.cs new file mode 100644 index 0000000..96868d9 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x5CF_11Response.cs @@ -0,0 +1,46 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5CF_11Response +{ + [ProtoMember(1)] public uint Field1 { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x5CF_11ResponseInfo Info { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5CF_11ResponseInfo +{ + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public uint Count { get; set; } + + [ProtoMember(7)] public List Requests { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x5CF_11ResponseRequests +{ + [ProtoMember(1)] public string TargetUid { get; set; } + + [ProtoMember(2)] public string SourceUid { get; set; } + + [ProtoMember(3)] public uint State { get; set; } + + [ProtoMember(4)] public uint Timestamp { get; set; } + + [ProtoMember(5)] public string Comment { get; set; } + + [ProtoMember(6)] public string Source { get; set; } + + [ProtoMember(7)] public uint SourceId { get; set; } + + [ProtoMember(8)] public uint SubSourceId { get; set; } + + // 剩下的我也猜不出来了 谁猜出来了谁PR吧 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6Response.cs new file mode 100644 index 0000000..f403eae --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6Response.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6Response +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x6D6_0Response Upload { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D6_2Response Download { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x6D6_3_4_5Response Delete { get; set; } + + [ProtoMember(5)] public OidbSvcTrpcTcp0x6D6_3_4_5Response Rename { get; set; } + + [ProtoMember(6)] public OidbSvcTrpcTcp0x6D6_3_4_5Response Move { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_0Response.cs new file mode 100644 index 0000000..7bafd6b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_0Response.cs @@ -0,0 +1,36 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// Resharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6_0Response +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } + + [ProtoMember(4)] public string UploadIp { get; set; } + + [ProtoMember(5)] public string ServerDns { get; set; } + + [ProtoMember(6)] public int BusId { get; set; } + + [ProtoMember(7)] public string FileId { get; set; } + + [ProtoMember(8)] public byte[] CheckKey { get; set; } + + [ProtoMember(9)] public byte[] FileKey { get; set; } + + [ProtoMember(10)] public bool BoolFileExist { get; set; } + + [ProtoMember(12)] public List UploadIpLanV4 { get; set; } + + [ProtoMember(13)] public List UploadIpLanV6 { get; set; } + + [ProtoMember(14)] public uint UploadPort { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_2Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_2Response.cs new file mode 100644 index 0000000..985098a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_2Response.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6_2Response +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } + + [ProtoMember(4)] public string DownloadIp { get; set; } + + [ProtoMember(5)] public string DownloadDns { get; set; } + + [ProtoMember(6)] public byte[] DownloadUrl { get; set; } + + [ProtoMember(7)] public byte[] FileSha1 { get; set; } + + [ProtoMember(8)] public byte[] FileSha3 { get; set; } // ? + + [ProtoMember(9)] public byte[] FileMd5 { get; set; } + + [ProtoMember(10)] public byte[] CookieVal { get; set; } + + [ProtoMember(11)] public string SaveFileName { get; set; } + + [ProtoMember(12)] public uint PreviewPort { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_3_4_5Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_3_4_5Response.cs new file mode 100644 index 0000000..ac64f35 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D6_3_4_5Response.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D6_3_4_5Response +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7Response.cs new file mode 100644 index 0000000..344520a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7Response.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7Response +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x6D7_0Response Create { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x6D7_1_2Response Delete { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D7_1_2Response Rename { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_0Response.cs new file mode 100644 index 0000000..a70f4db --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_0Response.cs @@ -0,0 +1,36 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_0Response +{ + [ProtoMember(1)] public int Retcode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x6D7_0ResponseFolderInfo FolderInfo { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_0ResponseFolderInfo +{ + [ProtoMember(1)] public string FolderId { get; set; } + + [ProtoMember(2)] public string FolderPath { get; set; } + + [ProtoMember(3)] public string FolderName { get; set; } + + [ProtoMember(4)] public uint Timestamp4 { get; set; } + + [ProtoMember(5)] public uint Timestamp5 { get; set; } + + [ProtoMember(6)] public uint OperatorUin6 { get; set; } + + [ProtoMember(7)] public uint OperatorUin9 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_1_2Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_1_2Response.cs new file mode 100644 index 0000000..ca45872 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D7_1_2Response.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D7_1_2Response +{ + [ProtoMember(1)] public Int32 Retcode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D8_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D8_1Response.cs new file mode 100644 index 0000000..8f3e84d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x6D8_1Response.cs @@ -0,0 +1,116 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1Response +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0x6D8_1ResponseList? List { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D8_1ResponseCount? Count { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x6D8_1ResponseSpace? Space { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseList +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string RetMsg { get; set; } + + [ProtoMember(3)] public string ClientWording { get; set; } + + [ProtoMember(4)] public bool IsEnd { get; set; } + + [ProtoMember(5)] public List? Items { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseCount +{ + [ProtoMember(4)] public uint FileCount { get; set; } + + [ProtoMember(6)] public uint LimitCount { get; set; } + + [ProtoMember(7)] public bool IsFull { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseSpace +{ + [ProtoMember(4)] public ulong TotalSpace { get; set; } + + [ProtoMember(5)] public ulong UsedSpace { get; set; } + + [ProtoMember(6)] public uint Field6 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseItem +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x6D8_1ResponseFolderInfo FolderInfo { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x6D8_1ResponseFileInfo FileInfo { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseFolderInfo +{ + [ProtoMember(1)] public string FolderId { get; set; } + + [ProtoMember(2)] public string ParentDirectoryId { get; set; } + + [ProtoMember(3)] public string FolderName { get; set; } + + [ProtoMember(4)] public uint CreateTime { get; set; } + + [ProtoMember(5)] public uint ModifiedTime { get; set; } + + [ProtoMember(6)] public uint CreatorUin { get; set; } + + [ProtoMember(7)] public string CreatorName { get; set; } + + [ProtoMember(8)] public uint TotalFileCount { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x6D8_1ResponseFileInfo +{ + [ProtoMember(1)] public string FileId { get; set; } + + [ProtoMember(2)] public string FileName { get; set; } + + [ProtoMember(3)] public ulong FileSize { get; set; } + + [ProtoMember(4)] public uint BusId { get; set; } + + [ProtoMember(5)] public ulong UploadedSize { get; set; } + + [ProtoMember(6)] public uint UploadedTime { get; set; } + + [ProtoMember(7)] public uint ExpireTime { get; set; } + + [ProtoMember(8)] public uint ModifiedTime { get; set; } + + [ProtoMember(9)] public uint DownloadedTimes { get; set; } + + [ProtoMember(10)] public byte[] FileSha1 { get; set; } + + [ProtoMember(12)] public byte[] FileMd5 { get; set; } + + [ProtoMember(14)] public string UploaderName { get; set; } + + [ProtoMember(15)] public uint UploaderUin { get; set; } + + [ProtoMember(16)] public string ParentDirectory { get; set; } + + [ProtoMember(17)] public uint Field17 { get; set; } + + [ProtoMember(22)] public string Field22 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x88D_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x88D_0Response.cs new file mode 100644 index 0000000..a8ceee0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x88D_0Response.cs @@ -0,0 +1,66 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x88D_0Response +{ + [ProtoMember(1)] + public OidbSvcTrpcTcp0x88D_0ResponseGroupInfo GroupInfo { get; set; } +} + +[ProtoContract] +public class OidbSvcTrpcTcp0x88D_0ResponseGroupInfo +{ + [ProtoMember(1)] + public ulong Uin { get; set; } + + [ProtoMember(3)] + public OidbSvcTrpcTcp0x88D_0ResponseResults Results { get; set; } +} + +[ProtoContract] +public class OidbSvcTrpcTcp0x88D_0ResponseResults +{ + [ProtoMember(1)] + public string OwnerUid { get; set; } + + [ProtoMember(2)] + public ulong CreateTime { get; set; } + + [ProtoMember(5)] + public ulong MaxMemberCount { get; set; } + + [ProtoMember(6)] + public ulong MemberCount { get; set; } + + [ProtoMember(10)] + public ulong Level { get; set; } + + [ProtoMember(15)] + public string Name { get; set; } + + [ProtoMember(16)] + public string NoticePreview { get; set; } + + [ProtoMember(21)] + public ulong Uin { get; set; } + + [ProtoMember(22)] + public ulong LastSequence { get; set; } + + [ProtoMember(23)] + public ulong LastMessageTime { get; set; } + + [ProtoMember(24)] + public string Question { get; set; } + + [ProtoMember(25)] + public string Answer { get; set; } + + [ProtoMember(29)] + public ulong MaxAdminCount { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x89A_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x89A_0Response.cs new file mode 100644 index 0000000..8a27040 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x89A_0Response.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x89A_0Response +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public string? ErorMsg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A0_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A0_1Response.cs new file mode 100644 index 0000000..0381c48 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A0_1Response.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x8A0_1Response +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public string? ErrorMsg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A7_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A7_0Response.cs new file mode 100644 index 0000000..a4fc459 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8A7_0Response.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x8A7_0Response +{ + [ProtoMember(1)] public bool CanAtAll { get; set; } + + [ProtoMember(2)] public uint RemainAtAllCountForUin { get; set; } + + [ProtoMember(3)] public uint RemainAtAllCountForGroup { get; set; } + + [ProtoMember(4)] public string? PromptMsg1 { get; set; } + + [ProtoMember(5)] public string? PromptMsg2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8FC_3Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8FC_3Response.cs new file mode 100644 index 0000000..cdf425b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x8FC_3Response.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0x8FC_3Response +{ + [ProtoMember(1)] public uint GroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x9154_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x9154_1Response.cs new file mode 100644 index 0000000..a29ce92 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x9154_1Response.cs @@ -0,0 +1,84 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// Resharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1Response +{ + [ProtoMember(1)] public int Field1 { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x9154_1ResponseContent CommonFace { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0x9154_1ResponseContent SpecialBigFace { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0x9154_1ResponsesMagicEmojiContent SpecialMagicFace { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponseContent +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x9154_1ResponseContentEmojiList[] EmojiList { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x9154_1ResponseContentResourceUrl? ResourceUrl { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponsesMagicEmojiContent +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x9154_1ResponsesMagicEmojiContentList Field1 { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x9154_1ResponseContentResourceUrl? ResourceUrl { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponsesMagicEmojiContentList +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0x9154_1ResponseContentEmoji[] EmojiList { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponseContentEmojiList +{ + [ProtoMember(1)] public string EmojiPackName { get; set; } + + [ProtoMember(2)] public OidbSvcTrpcTcp0x9154_1ResponseContentEmoji[] EmojiDetail { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponseContentEmoji +{ + [ProtoMember(1)] public string QSid { get; set; } + + [ProtoMember(2)] public string? QDes { get; set; } + + [ProtoMember(3)] public string? EMCode { get; set; } + + [ProtoMember(4)] public int? QCid { get; set; } + + [ProtoMember(5)] public int? AniStickerType { get; set; } + + [ProtoMember(6)] public int? AniStickerPackId { get; set; } + + [ProtoMember(7)] public int? AniStickerId { get; set; } + + [ProtoMember(8)] public OidbSvcTrpcTcp0x9154_1ResponseContentResourceUrl? Url { get; set; } + + [ProtoMember(9)] public string[]? EmojiNameAlias { get; set; } + + [ProtoMember(10)] public int? Unknown10 { get; set; } + + [ProtoMember(13)] public int? AniStickerWidth { get; set; } + + [ProtoMember(14)] public int? AniStickerHeight { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x9154_1ResponseContentResourceUrl +{ + [ProtoMember(1)] public string? BaseUrl { get; set; } + + [ProtoMember(2)] public string? AdvUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs new file mode 100644 index 0000000..27792ea --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929B_0Response +{ + [ProtoMember(1)] uint Field1 { get; set; } //1 complete ,2 wait + + [ProtoMember(2)] uint? Field2 { get; set; } //319 + + [ProtoMember(3)] uint Field3 { get; set; } //20 + + [ProtoMember(4)] public MsgInfo? MsgInfo { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs new file mode 100644 index 0000000..7a1f29d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 +[ProtoContract] +internal class OidbSvcTrpcTcp0x929C_0Response +{ + [ProtoMember(1)] public List Property { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929C_0ResponseProperty +{ + [ProtoMember(1)] public string CharacterId { get; set; } + + [ProtoMember(2)] public string CharacterName { get; set; } + + [ProtoMember(3)] public string CharacterVoiceUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs new file mode 100644 index 0000000..a43eb06 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0Response +{ + [ProtoMember(1)] public List Property { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0ResponseKey +{ + [ProtoMember(1)] public string Type { get; set; } + [ProtoMember(2)] public List Vulue { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0ResponseProperty +{ + [ProtoMember(1)] public string CharacterId { get; set; } + + [ProtoMember(2)] public string CharacterName { get; set; } + + [ProtoMember(3)] public string CharacterVoiceUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs new file mode 100644 index 0000000..3c7b9fd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs @@ -0,0 +1,72 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE07_0_Response +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } + + [ProtoMember(3)] public string Wording { get; set; } + + [ProtoMember(10)] public OcrRspBody OcrRspBody { get; set; } +} + +[ProtoContract] +internal class OcrRspBody +{ + [ProtoMember(1)] public List TextDetections { get; set; } + + [ProtoMember(2)] public string Language { get; set; } + + [ProtoMember(3)] public string RequestId { get; set; } + + [ProtoMember(101)] public List OcrLanguageList { get; set; } + + [ProtoMember(102)] public List DstTranslateLanguageList { get; set; } + + [ProtoMember(103)] public List LanguageList { get; set; } + + [ProtoMember(111)] public uint AfterCompressWeight { get; set; } + + [ProtoMember(112)] public uint AfterCompressHeight { get; set; } +} + +[ProtoContract] +internal class TextDetection +{ + [ProtoMember(1)] public string DetectedText { get; set; } + + [ProtoMember(2)] public uint Confidence { get; set; } + + [ProtoMember(3)] public Polygon Polygon { get; set; } + + [ProtoMember(4)] public string AdvancedInfo { get; set; } +} + +[ProtoContract] +internal class Polygon +{ + [ProtoMember(1)] public List Coordinates { get; set; } +} + +[ProtoContract] +internal class Coordinate +{ + [ProtoMember(1)] public int X { get; set; } + + [ProtoMember(2)] public int Y { get; set; } +} + +[ProtoContract] +internal class Language +{ + [ProtoMember(1)] public string LanguageCode { get; set; } + + [ProtoMember(2)] public string LanguageDesc { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37Response.cs new file mode 100644 index 0000000..e91f1f2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37Response.cs @@ -0,0 +1,64 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// Resharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37Response +{ + [ProtoMember(1)] public uint Command { get; set; } // 1700 + + [ProtoMember(2)] public int Seq { get; set; } // 0 + + [ProtoMember(19)] public ApplyUploadRespV3 Upload { get; set; } + + [ProtoMember(101)] public int BusinessId { get; set; } // 3 + + [ProtoMember(102)] public int ClientType { get; set; } // 1 + + [ProtoMember(200)] public int FlagSupportMediaPlatform { get; set; } // 1 +} + +[ProtoContract] +internal class ApplyUploadRespV3 +{ + [ProtoMember(10)] public int RetCode { get; set; } + + [ProtoMember(20)] public string RetMsg { get; set; } + + [ProtoMember(30)] public long TotalSpace { get; set; } + + [ProtoMember(40)] public long UsedSpace { get; set; } + + [ProtoMember(50)] public long UploadedSize { get; set; } + + [ProtoMember(60)] public string UploadIp { get; set; } + + [ProtoMember(70)] public string UploadDomain { get; set; } + + [ProtoMember(80)] public uint UploadPort { get; set; } + + [ProtoMember(90)] public string Uuid { get; set; } + + [ProtoMember(100)] public byte[] UploadKey { get; set; } + + [ProtoMember(110)] public bool BoolFileExist { get; set; } + + [ProtoMember(120)] public int PackSize { get; set; } + + [ProtoMember(130)] public List UploadIpList { get; set; } + + [ProtoMember(140)] public int UploadHttpsPort { get; set; } + + [ProtoMember(150)] public string UploadHttpsDomain { get; set; } + + [ProtoMember(160)] public string UploadDns { get; set; } + + [ProtoMember(170)] public string UploadLanip { get; set; } + + [ProtoMember(200)] public string FileAddon { get; set; } + + [ProtoMember(220)] public byte[] MediaPlatformUploadKey { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37_1200Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37_1200Response.cs new file mode 100644 index 0000000..57e018f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE37_1200Response.cs @@ -0,0 +1,80 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// Resharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1200Response +{ + [ProtoMember(1)] public uint Command { get; set; } + + [ProtoMember(2)] public uint SubCommand { get; set; } + + [ProtoMember(14)] public OidbSvcTrpcTcp0xE37_1200ResponseBody Body { get; set; } + + [ProtoMember(50)] public uint Field50 { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1200ResponseBody +{ + [ProtoMember(10)] public uint Field10 { get; set; } + + [ProtoMember(20)] public string State { get; set; } + + [ProtoMember(30)] public OidbSvcTrpcTcp0xE37_1200Result Result { get; set; } + + [ProtoMember(40)] public OidbSvcTrpcTcp0xE37_1200Metadata Metadata { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1200Result +{ + [ProtoMember(20)] public string Server { get; set; } + + [ProtoMember(40)] public uint Port { get; set; } + + [ProtoMember(50)] public string Url { get; set; } + + [ProtoMember(60)] public List AdditionalServer { get; set; } + + [ProtoMember(80)] public uint SsoPort { get; set; } + + [ProtoMember(90)] public string SsoUrl { get; set; } + + [ProtoMember(120)] public byte[] Extra { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1200Metadata +{ + [ProtoMember(1)] public uint Uin { get; set; } + + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public uint Size { get; set; } + + [ProtoMember(5)] public uint Timestamp { get; set; } + + [ProtoMember(6)] public string FileUuid { get; set; } + + [ProtoMember(7)] public string FileName { get; set; } + + [ProtoMember(100)] public byte[] Field100 { get; set; } + + [ProtoMember(101)] public byte[] Field101 { get; set; } + + [ProtoMember(110)] public uint Field110 { get; set; } + + [ProtoMember(130)] public uint Timestamp1 { get; set; } + + [ProtoMember(140)] public string FileHash { get; set; } + + [ProtoMember(141)] public byte[] Field141 { get; set; } // identical to Field100 + + [ProtoMember(142)] public byte[] Field142 { get; set; } // identical to Field101 +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xEB7_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xEB7_1Response.cs new file mode 100644 index 0000000..fe4b764 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xEB7_1Response.cs @@ -0,0 +1,27 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class OidbSvcTrpcTcp0xEB7_1Response +{ + [ProtoMember(2)] public BodyClass Body { get; set; } + + [ProtoContract] + internal class BodyClass + { + [ProtoMember(2)] public ResultClass Result { get; set; } + + [ProtoContract] + internal class ResultClass + { + [ProtoMember(1)] public string Title { get; set; } // 今日已成功打卡 + [ProtoMember(2)] public string KeepDayText { get; set; } // 已打卡N天 + [ProtoMember(3)] public string[] ClockInInfo1 { get; set; } // ["群内排名第N位", "[clock in timestamp (second)]"] + [ProtoMember(4)] public string DetailUrl { get; set; } // https://qun.qq.com/v2/signin/detail?... + } + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xF8E_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xF8E_1Response.cs new file mode 100644 index 0000000..bf756f0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xF8E_1Response.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xF8E_1Response +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xF8E_1ResponseBody Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xF8E_1ResponseBody +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public uint Sequence { get; set; } + + [ProtoMember(6)] public string Preview { get; set; } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFD4_1Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFD4_1Response.cs new file mode 100644 index 0000000..0bfb3c6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFD4_1Response.cs @@ -0,0 +1,29 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFD4_1Response +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0xFD4_1ResponseUin? Next { get; set; } + + [ProtoMember(3)] public uint DisplayFriendCount { get; set; } + + [ProtoMember(6)] public uint Timestamp { get; set; } + + [ProtoMember(7)] public uint SelfUin { get; set; } + + [ProtoMember(101)] public List Friends { get; set; } + + [ProtoMember(102)] public List Groups { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFD4_1ResponseUin +{ + [ProtoMember(1)] public uint Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE1_2Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE1_2Response.cs new file mode 100644 index 0000000..3c6d151 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE1_2Response.cs @@ -0,0 +1,81 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using ProtoBuf; + +#pragma warning disable CS8618 +// Resharper Disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE1_2Response +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xFE1_2ResponseBody Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE1_2ResponseBody +{ + // [ProtoMember(1)] public string Uid { get; set; } = ""; + + [ProtoMember(2)] public OidbSvcTrpcTcp0xFE1_2ResponseProperty Properties { get; set; } + + [ProtoMember(3)] public uint Uin { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE1_2ResponseProperty +{ + [ProtoMember(1)] public List NumberProperties { get; set; } + + [ProtoMember(2)] public List BytesProperties { get; set; } +} + +[ProtoContract] +public class CustomStatus +{ + [ProtoMember(1)] public uint FaceId { get; set; } + + [ProtoMember(2)] public string? Msg { get; set; } +} + +[ProtoContract] +public class Avatar +{ + [ProtoMember(5)] public string? Url { get; set; } +} + +[ProtoContract] +public class Business +{ + [ProtoMember(3)] public BusinessBody? Body { get; set; } +} + +[ProtoContract] +public class BusinessBody +{ + [ProtoMember(1)] public string? Msg { get; set; } + + [ProtoMember(3)] public List? Lists { get; set; } +} + +[ProtoContract] +public class BusinessList +{ + [ProtoMember(1)] public uint Type { get; set; } + + [ProtoMember(2)] internal uint Field2 { get; set; } + + [ProtoMember(3)] public uint IsYear { get; set; } // 是否年费 + + [ProtoMember(4)] public uint Level { get; set; } + + [ProtoMember(5)] public uint IsPro { get; set; } // 是否为超级 + + [ProtoMember(6)] internal string? Icon1 { get; set; } + + [ProtoMember(7)] internal string? Icon2 { get; set; } + + public string? Icon => !string.IsNullOrEmpty(Icon1) ? Icon1 : Icon2; + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE5_2Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE5_2Response.cs new file mode 100644 index 0000000..a322e8a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE5_2Response.cs @@ -0,0 +1,46 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// Resharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Response +{ + [ProtoMember(2)] public List Groups { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Group +{ + [ProtoMember(3)] public uint GroupUin { get; set; } + + [ProtoMember(4)] public OidbSvcTrpcTcp0xFE5_2GroupInfo Info { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2GroupInfo +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xFE5_2Member GroupOwner { get; set; } + + [ProtoMember(2)] public uint CreatedTime { get; set; } + + [ProtoMember(3)] public uint MemberMax { get; set; } + + [ProtoMember(4)] public uint MemberCount { get; set; } + + [ProtoMember(5)] public string GroupName { get; set; } + + [ProtoMember(18)] public string? Description { get; set; } + + [ProtoMember(19)] public string? Question { get; set; } + + [ProtoMember(30)] public string? Announcement { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE5_2Member +{ + [ProtoMember(2)] public string Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE7_3Response.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE7_3Response.cs new file mode 100644 index 0000000..4d0866b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xFE7_3Response.cs @@ -0,0 +1,67 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE7_3Response +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(2)] public List Members { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(5)] public uint MemberChangeSeq { get; set; } + + [ProtoMember(6)] public uint MemberCardChangeSeq { get; set; } + + [ProtoMember(15)] public string? Token { get; set; } // for the next page +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE7_3Member +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0xFE7_3Uin Uin { get; set; } + + [ProtoMember(10)] public string MemberName { get; set; } + + [ProtoMember(17)] public string? SpecialTitle { get; set; } + + [ProtoMember(11)] public OidbSvcTrpcTcp0xFE7_3Card MemberCard { get; set; } + + [ProtoMember(12)] public OidbSvcTrpcTcp0xFE7_3Level? Level { get; set; } + + [ProtoMember(100)] public uint JoinTimestamp { get; set; } + + [ProtoMember(101)] public uint LastMsgTimestamp { get; set; } + + [ProtoMember(102)] public uint? ShutUpTimestamp { get; set; } + + [ProtoMember(107)] public uint Permission { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE7_3Uin +{ + [ProtoMember(2)] public string Uid { get; set; } + + [ProtoMember(4)] public uint Uin { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE7_3Card +{ + [ProtoMember(2)] public string? MemberCard { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xFE7_3Level +{ + [ProtoMember(1)] public List? Infos { get; set; } + + [ProtoMember(2)] public uint Level { get; set; } +} + diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/ServicePacker.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/ServicePacker.cs new file mode 100644 index 0000000..12d18f8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/ServicePacker.cs @@ -0,0 +1,77 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Binary; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets; + +internal static class ServicePacker +{ + public static BinaryPacket BuildProtocol13(byte[] packet, BotKeystore keystore, string command, uint sequence) + { + var frame = new BinaryPacket(); + + using var stream = new MemoryStream(); + var uid = new NTPacketUid { Uid = keystore.Uid }; + Serializer.Serialize(stream, uid); + var uidBytes = keystore.Uid == null ? Array.Empty() : stream.ToArray(); + + frame.Barrier(w => w + .WriteUint(13) + .WriteByte(0) // flag + .WriteUint(sequence) + .WriteByte(0) + .WriteString("0", Prefix.Uint32 | Prefix.WithPrefix) + .Barrier(w => w + .WriteString(command, Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: Unknown + .WriteBytes(uidBytes, Prefix.Uint32 | Prefix.WithPrefix), Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(packet.ToArray(), Prefix.Uint32 | Prefix.WithPrefix), Prefix.Uint32 | Prefix.WithPrefix); + + return frame; + } + + /// + /// Build Universal Packet, every service should derive from this, protocol 12 only + /// + public static BinaryPacket BuildProtocol12(BinaryPacket packet, BotKeystore keystore) + { + var frame = new BinaryPacket(); + + frame.Barrier(w => w + .WriteUint(12) // protocolVersion + .WriteByte((byte)(keystore.Session.D2.Length == 0 ? 2 : 1)) // flag + .WriteBytes(keystore.Session.D2, Prefix.Uint32 | Prefix.WithPrefix) + .WriteByte(0) // unknown + .WriteString(keystore.Uin.ToString(), Prefix.Uint32 | Prefix.WithPrefix) // 帅 + .WriteBytes(keystore.TeaImpl.Encrypt(packet.ToArray(), keystore.Session.D2Key).AsSpan()), Prefix.Uint32 | Prefix.WithPrefix); + + return frame; + } + + /// + /// Parse Universal Packet, every service should derive from this, protocol 12 and 13 + /// + public static BinaryPacket Parse(BinaryPacket packet, BotKeystore keystore) + { + uint length = packet.ReadUint(); + uint protocol = packet.ReadUint(); + byte authFlag = packet.ReadByte(); + byte flag = packet.ReadByte(); + string uin = packet.ReadString(Prefix.Uint32 | Prefix.WithPrefix); + + if (protocol != 12 && protocol != 13) throw new Exception($"Unrecognized protocol: {protocol}"); + if (uin != keystore.Uin.ToString() && protocol == 12) throw new Exception($"Uin mismatch: {uin} != {keystore.Uin}"); + + var encrypted = packet.ReadBytes((int)packet.Remaining); + var decrypted = authFlag switch + { + 0 => encrypted, + 1 => keystore.TeaImpl.Decrypt(encrypted, keystore.Session.D2Key), + 2 => keystore.TeaImpl.Decrypt(encrypted, new byte[16]), + _ => throw new Exception($"Unrecognized auth flag: {authFlag}") + }; + + return new BinaryPacket(decrypted); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacker.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacker.cs new file mode 100644 index 0000000..ffea2c2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacker.cs @@ -0,0 +1,80 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Compression; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; +using Lagrange.Core.Utility.Sign; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets; + +internal static class SsoPacker +{ + /// + /// Build Protocol 12 SSO packet + /// + public static BinaryPacket Build(SsoPacket packet, BotAppInfo appInfo, BotDeviceInfo device, BotKeystore keystore, SignProvider signProvider) + { + var writer = new BinaryPacket(); + + var sign = signProvider.Sign(packet.Command, packet.Sequence, packet.Payload.ToArray(), out var extra, out var token); + var signature = new SsoReserveFields + { + SecInfo = sign == null ? null : new SsoSecureInfo + { + SecSign = sign, + SecToken = token, + SecExtra = extra + }, + TraceParent = StringGen.GenerateTrace(), + Uid = keystore.Uid + }; + var stream = new MemoryStream(); + Serializer.Serialize(stream, signature); + + writer.Barrier( w => w// Barrier is used to calculate the length of the packet header only + .WriteUint(packet.Sequence) // sequence + .WriteUint((uint)appInfo.SubAppId) // appId + .WriteUint(2052) // LocaleId + .WriteBytes("020000000000000000000000".UnHex().AsSpan()) + .WriteBytes(keystore.Session.Tgt, Prefix.Uint32 | Prefix.WithPrefix) + .WriteString(packet.Command, Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: unknown + .WriteString(device.Guid.ToByteArray().Hex(true), Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: unknown + .WriteString(appInfo.CurrentVersion, Prefix.Uint16 | Prefix.WithPrefix) // Actually at wtlogin.trans_emp, this string is empty and only prefix 00 02 is given, but we can just simply ignore that situation + .WriteBytes(stream.ToArray(), Prefix.Uint32 | Prefix.WithPrefix), Prefix.Uint32 | Prefix.WithPrefix); // packet end + + return writer.WriteBytes(packet.Payload.ToArray(), Prefix.Uint32 | Prefix.WithPrefix); + } + + /// + /// Parse Protocol 12 SSO packet + /// + public static SsoPacket Parse(BinaryPacket packet) + { + var head = packet.ReadBytes(Prefix.Uint32 | Prefix.WithPrefix); + var headReader = new BinaryPacket(head); + + uint sequence = headReader.ReadUint(); + int retCode = headReader.ReadInt(); + string extra = headReader.ReadString(Prefix.Uint32 | Prefix.WithPrefix); + string command = headReader.ReadString(Prefix.Uint32 | Prefix.WithPrefix); + var msgCookie = headReader.ReadBytes(Prefix.Uint32 | Prefix.WithPrefix); + int isCompressed = headReader.ReadInt(); + var reserveField = headReader.ReadBytes(Prefix.Uint32 | Prefix.WithPrefix); + + var body = packet.ReadBytes(Prefix.Uint32 | Prefix.WithPrefix).ToArray(); + var raw = isCompressed switch + { + 0 or 4 => body, + 1 => ZCompression.ZDecompress(body, false), + _ => throw new Exception($"Unknown compression type: {isCompressed}") + }; + + return retCode == 0 + ? new SsoPacket(12, command, sequence, raw) + : new SsoPacket(12, command, sequence, retCode, extra); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacket.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacket.cs new file mode 100644 index 0000000..c38073e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/SsoPacket.cs @@ -0,0 +1,34 @@ +namespace Lagrange.Core.Internal.Packets; + +internal class SsoPacket +{ + public byte PacketType { get; set; } + + public string Command { get; } + + public uint Sequence { get; } + + public byte[] Payload { get; } + + public int RetCode { get; } + + public string? Extra { get; } + + public SsoPacket(byte packetType, string command, uint sequence, byte[] payload) + { + PacketType = packetType; + Command = command; + Sequence = sequence; + Payload = payload; + } + + public SsoPacket(byte packetType, string command, uint sequence, int retCode, string extra) + { + PacketType = packetType; + Command = command; + Sequence = sequence; + RetCode = retCode; + Payload = Array.Empty(); + Extra = extra; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTOS.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTOS.cs new file mode 100644 index 0000000..d007e45 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTOS.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class NTOS +{ + [ProtoMember(1)] public string OS { get; set; } + + [ProtoMember(2)] public string Name { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTPacketUid.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTPacketUid.cs new file mode 100644 index 0000000..ac7d4a6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTPacketUid.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class NTPacketUid +{ + [ProtoMember(16)] public string? Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSsoHeartBeat.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSsoHeartBeat.cs new file mode 100644 index 0000000..c47d59b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSsoHeartBeat.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +// ReSharper disable once InconsistentNaming + +/// +/// trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat +/// +[ProtoContract] +internal class NTSsoHeartBeat +{ + [ProtoMember(1)] public int Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEvent.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEvent.cs new file mode 100644 index 0000000..854b0ca --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEvent.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class NTSysEvent +{ + [ProtoMember(1)] public string Ip { get; set; } + + [ProtoMember(2)] public long Sid { get; set; } + + [ProtoMember(3)] public NTSysEventSub Sub { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEventSub.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEventSub.cs new file mode 100644 index 0000000..0d49ff6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/NTSysEventSub.cs @@ -0,0 +1,24 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class NTSysEventSub +{ + [ProtoMember(2)] public long State { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public long Field4 { get; set; } + + [ProtoMember(5)] public long Uin { get; set; } + + [ProtoMember(6)] public int Flag { get; set; } + + [ProtoMember(7)] public int On { get; set; } + + [ProtoMember(8)] public uint GroupUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineBusinessInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineBusinessInfo.cs new file mode 100644 index 0000000..20d83de --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineBusinessInfo.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class OnlineBusinessInfo +{ + [ProtoMember(1)] public uint NotifySwitch { get; set; } + + [ProtoMember(2)] public uint BindUinNotifySwitch { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineDeviceInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineDeviceInfo.cs new file mode 100644 index 0000000..e5886ad --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/OnlineDeviceInfo.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class OnlineDeviceInfo +{ + [ProtoMember(1)] public string User { get; set; } + + [ProtoMember(2)] public string Os { get; set; } + + [ProtoMember(3)] public string OsVer { get; set; } + + [ProtoMember(4)] public string? VendorName { get; set; } + + [ProtoMember(5)] public string OsLower { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfo.cs new file mode 100644 index 0000000..02a2b66 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfo.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +/// +/// trpc.qq_new_tech.status_svc.StatusService.Register +/// +[ProtoContract] +internal class RegisterInfo +{ + [ProtoMember(1)] public string? Guid { get; set; } + + [ProtoMember(2)] public int? KickPC { get; set; } + + [ProtoMember(3)] public string? CurrentVersion { get; set; } + + [ProtoMember(4)] public int? IsFirstRegisterProxyOnline { get; set; } + + [ProtoMember(5)] public int? LocaleId { get; set; } // 2052 + + [ProtoMember(6)] public OnlineDeviceInfo? Device { get; set; } + + [ProtoMember(7)] public int? SetMute { get; set; } + + [ProtoMember(8)] public int? RegisterVendorType { get; set; } + + [ProtoMember(9)] public int? RegType { get; set; } + + [ProtoMember(10)] public OnlineBusinessInfo? BusinessInfo { get; set; } + + [ProtoMember(11)] public int? BatteryStatus { get; set; } + + [ProtoMember(12)] public uint? Field12 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfoResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfoResponse.cs new file mode 100644 index 0000000..4ca96a8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/RegisterInfoResponse.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class RegisterInfoResponse +{ + [ProtoMember(2)] public string? Message { get; set; } + + [ProtoMember(3)] public uint Timestamp { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/ServiceKickNTResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/ServiceKickNTResponse.cs new file mode 100644 index 0000000..0a59c1f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/ServiceKickNTResponse.cs @@ -0,0 +1,22 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class ServiceKickNTResponse +{ + [ProtoMember(1)] public uint Uin { get; set; } + + [ProtoMember(3)] public string Tips { get; set; } + + [ProtoMember(4)] public string Title { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } + + [ProtoMember(6)] public int Field6 { get; set; } + + [ProtoMember(8)] public int Field8 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSync.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSync.cs new file mode 100644 index 0000000..a9ff52d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSync.cs @@ -0,0 +1,68 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +/// +/// trpc.msg.register_proxy.RegisterProxy.SsoInfoSync +/// +[ProtoContract] +internal class SsoInfoSyncRequest +{ + [ProtoMember(1)] public uint SyncFlag { get; set; } + + [ProtoMember(2)] public uint? ReqRandom { get; set; } + + [ProtoMember(4)] public uint? CurActiveStatus { get; set; } + + [ProtoMember(5)] public ulong? GroupLastMsgTime { get; set; } + + [ProtoMember(6)] public SsoC2CInfoSync? C2CInfoSync { get; set; } + + [ProtoMember(8)] public NormalConfig? NormalConfig { get; set; } + + [ProtoMember(9)] public RegisterInfo? RegisterInfo { get; set; } + + [ProtoMember(10)] public UnknownStructure? UnknownStructure { get; set; } + + [ProtoMember(11)] public CurAppState? AppState { get; set; } +} + +[ProtoContract] +internal class SsoC2CMsgCookie +{ + [ProtoMember(1)] public ulong C2CLastMsgTime { get; set; } +} + +[ProtoContract] +internal class SsoC2CInfoSync +{ + [ProtoMember(1)] public SsoC2CMsgCookie? C2CMsgCookie { get; set; } + + [ProtoMember(2)] public ulong C2CLastMsgTime { get; set; } + + [ProtoMember(3)] public SsoC2CMsgCookie? LastC2CMsgCookie { get; set; } +} + +[ProtoContract] +internal class NormalConfig +{ + [ProtoMember(1)] public Dictionary? IntCfg { get; set; } +} + +[ProtoContract] +internal class CurAppState +{ + [ProtoMember(1)] public uint? IsDelayRequest { get; set; } + + [ProtoMember(2)] public uint? AppStatus { get; set; } + + [ProtoMember(3)] public uint? SilenceStatus { get; set; } +} + +[ProtoContract] +internal class UnknownStructure +{ + [ProtoMember(1)] public uint? GroupCode { get; set; } + + [ProtoMember(2)] public uint Flag2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSyncResponse.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSyncResponse.cs new file mode 100644 index 0000000..60d4f30 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoInfoSyncResponse.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class SsoInfoSyncResponse +{ + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(6)] public uint Field6 { get; set; } + + [ProtoMember(7)] public RegisterInfoResponse? RegisterInfoResponse { get; set; } + + [ProtoMember(9)] public uint Field9 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs new file mode 100644 index 0000000..12e93e4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoReserveFields.cs @@ -0,0 +1,49 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +[ProtoContract] +internal class SsoReserveFields +{ + [ProtoMember(4)] public byte[]? ClientIpcookie { get; set; } + + [ProtoMember(9)] public uint? Flag { get; set; } + + [ProtoMember(10)] public uint? EnvId { get; set; } + + [ProtoMember(11)] public uint? LocaleId { get; set; } + + [ProtoMember(12)] public string? Qimei { get; set; } + + [ProtoMember(13)] public byte[]? Env { get; set; } + + [ProtoMember(14)] public uint? NewconnFlag { get; set; } + + [ProtoMember(15)] public string? TraceParent { get; set; } + + [ProtoMember(16)] public string? Uid { get; set; } + + [ProtoMember(18)] public uint? Imsi { get; set; } + + [ProtoMember(19)] public uint? NetworkType { get; set; } + + [ProtoMember(20)] public uint? IpStackType { get; set; } + + [ProtoMember(21)] public uint? MsgType { get; set; } + + [ProtoMember(22)] public string? TrpcRsp { get; set; } + + [ProtoMember(23)] public Dictionary? TransInfo { get; set; } + + [ProtoMember(24)] public SsoSecureInfo? SecInfo { get; set; } + + [ProtoMember(25)] public uint? SecSigFlag { get; set; } + + [ProtoMember(26)] public uint? NtCoreVersion { get; set; } + + [ProtoMember(27)] public uint? SsoRouteCost { get; set; } + + [ProtoMember(28)] public uint? SsoIpOrigin { get; set; } + + [ProtoMember(30)] public byte[]? PresureToken { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs new file mode 100644 index 0000000..c9a5e84 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/System/SsoSecureInfo.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.System; + +/// +/// wtlogin.trans_emp and wtlogin.login packet, has tag of C201 in ushort and length of 0x35 in byte +/// 没错 这个就是臭名昭著的0C03算法 每一个包都有这个几把 +/// +[ProtoContract] +internal class SsoSecureInfo +{ + [ProtoMember(1)] public byte[]? SecSign { get; set; } + + [ProtoMember(2)] public string? SecToken { get; set; } + + [ProtoMember(3)] public byte[]? SecExtra { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv100.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv100.cs new file mode 100644 index 0000000..df0b49e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv100.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x100)] +internal class Tlv100 : TlvBody +{ + public Tlv100(BotAppInfo appInfo) + { + AppId = (uint)appInfo.AppId; + SubAppId = (uint)appInfo.SubAppId; + AppClientVersion = appInfo.AppClientVersion; + MainSigMap = appInfo.MainSigMap; + } + + [BinaryProperty] public ushort DbBufVersion { get; set; } = 0; // originally 0x1 + + [BinaryProperty] public uint SsoVersion { get; set; } = 0x00000005; + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty] public uint SubAppId { get; set; } + + [BinaryProperty] public uint AppClientVersion { get; set; } + + [BinaryProperty] public uint MainSigMap { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv103.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv103.cs new file mode 100644 index 0000000..f81dfde --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv103.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X103)] +internal class Tlv103 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv106.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv106.cs new file mode 100644 index 0000000..bdf2c54 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv106.cs @@ -0,0 +1,29 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x106)] +internal class Tlv106 : TlvBody +{ + /// + /// manually construct Tlv106 by tempPassword, from TlvQrCode18, not through dependency injection + /// This field does not only use as the request, but also response + /// Response should be referred to + + /// Password is depreciated, so such field is now injected through dependency injection + /// + public Tlv106(BotKeystore keystore) => Temp = keystore.Session.TempPassword ?? throw new ArgumentNullException(nameof(keystore.Session.TempPassword)); + + [BinaryProperty(Prefix.None)] public byte[] Temp { get; set; } +} + +[Tlv(0x106, true)] +internal class Tlv106Response : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Temp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv107.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv107.cs new file mode 100644 index 0000000..cdbf70b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv107.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x107)] +internal class Tlv107 : TlvBody +{ + [BinaryProperty] public ushort PicType { get; set; } = 0x0001; + + [BinaryProperty] public byte CapType { get; set; } = 0x0D; + + [BinaryProperty] public ushort PicSize { get; set; } = 0x0000; + + [BinaryProperty] public byte RetType { get; set; } = 0x01; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv108.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv108.cs new file mode 100644 index 0000000..7cd4452 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv108.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x108)] +internal class Tlv108 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10A.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10A.cs new file mode 100644 index 0000000..2192111 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10A.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x10A)] +internal class Tlv10A : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Tgt { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10C.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10C.cs new file mode 100644 index 0000000..3e14865 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10C.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x10C)] +internal class Tlv10C : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10D.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10D.cs new file mode 100644 index 0000000..6295640 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10D.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X10D)] +internal class Tlv10D : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10E.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10E.cs new file mode 100644 index 0000000..d6a14b8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv10E.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X10E)] +internal class Tlv10E : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11.cs new file mode 100644 index 0000000..f49f1eb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x011)] +internal class Tlv11 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv114.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv114.cs new file mode 100644 index 0000000..83f1534 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv114.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X114)] +internal class Tlv114 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv116.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv116.cs new file mode 100644 index 0000000..d9488d3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv116.cs @@ -0,0 +1,26 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x116)] +internal class Tlv116 : TlvBody +{ + public Tlv116(BotAppInfo appInfo) => SubSigMap = appInfo.SubSigMap; + + private static readonly uint[] AppIds = { }; + + [BinaryProperty] public byte Version { get; set; } = 0; + + [BinaryProperty] public uint MiscBitmap { get; set; } = 12058620; + + [BinaryProperty] public uint SubSigMap { get; set; } + + [BinaryProperty] public byte AppIdCount { get; set; } = (byte)AppIds.Length; + + [BinaryProperty(Prefix.None)] public byte[] AppIdBytes => + AppIds.Select(x => BitConverter.GetBytes(x, false)).SelectMany(x => x).ToArray(); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv118.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv118.cs new file mode 100644 index 0000000..f836e50 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv118.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X118)] +internal class Tlv118 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv119.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv119.cs new file mode 100644 index 0000000..980f58b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv119.cs @@ -0,0 +1,15 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x119)] +internal class Tlv119 : TlvBody +{ + /// + /// The encrypted TLV data that should be decrypted with the TGTGT key. + /// + [BinaryProperty(Prefix.None)] public byte[] EncryptedTlv { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11A.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11A.cs new file mode 100644 index 0000000..c85bf88 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11A.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X11A)] +internal class Tlv11A : TlvBody +{ + [BinaryProperty] public ushort FaceId { get; set; } + + [BinaryProperty] public byte Age { get; set; } + + [BinaryProperty] public byte Gender { get; set; } + + [BinaryProperty(Prefix.Uint8 | Prefix.None)] public string Nickname { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11F.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11F.cs new file mode 100644 index 0000000..31c79eb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv11F.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X11F)] +internal class Tlv11F : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv12.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv12.cs new file mode 100644 index 0000000..84751bf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv12.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x012)] +internal class Tlv12 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv124.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv124.cs new file mode 100644 index 0000000..e17e388 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv124.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x124)] +internal class Tlv124 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Field0 { get; set; } = new byte[12]; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv128.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv128.cs new file mode 100644 index 0000000..7005052 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv128.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x128)] +internal class Tlv128 : TlvBody +{ + public Tlv128(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + Os = appInfo.Os; + Guid = deviceInfo.Guid.ToByteArray(); + } + + [BinaryProperty] public ushort Const0 { get; set; } = 0; + + [BinaryProperty] public byte GuidNew { get; set; } = 0; + + [BinaryProperty] public byte GuidAvailable { get; set; } = 0; + + [BinaryProperty] public byte GuidChanged { get; set; } = 0; + + [BinaryProperty] public uint GuidFlag { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Os { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public byte[] Guid { get; set; } + + [BinaryProperty] public ushort Const1 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv130.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv130.cs new file mode 100644 index 0000000..5c05e0c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv130.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X130)] +internal class Tlv130 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv133.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv133.cs new file mode 100644 index 0000000..0d106e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv133.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X133)] +internal class Tlv133 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv134.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv134.cs new file mode 100644 index 0000000..33e6474 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv134.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X134)] +internal class Tlv134 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv138.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv138.cs new file mode 100644 index 0000000..628a2b4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv138.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X138)] +internal class Tlv138 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv141.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv141.cs new file mode 100644 index 0000000..3db4dac --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv141.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x141)] +internal class Tlv141 : TlvBody +{ + [BinaryProperty] public ushort Version { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Unknown { get; set; } = "Unknown"; + + [BinaryProperty] public ushort NetworkType { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Apn { get; set; } = ""; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv142.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv142.cs new file mode 100644 index 0000000..a065c57 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv142.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x142)] +internal class Tlv142 : TlvBody +{ + public Tlv142(BotAppInfo appInfo) => PackageName = appInfo.PackageName; + + [BinaryProperty] public ushort Version { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv143.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv143.cs new file mode 100644 index 0000000..eda7a68 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv143.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X143)] +internal class Tlv143 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] D2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv144.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv144.cs new file mode 100644 index 0000000..79db94a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv144.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x144)] +[TlvEncrypt(TlvEncryptAttribute.KeyType.TgtgtKey)] +internal class Tlv144 : TlvBody +{ + public Tlv144(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + TlvCount = 4; + Tlv16E = new TlvPacket(0x16E, new Tlv16E(deviceInfo)).ToArray(); + Tlv147 = new TlvPacket(0x147, new Tlv147(appInfo)).ToArray(); + Tlv128 = new TlvPacket(0x128, new Tlv128(appInfo, deviceInfo)).ToArray(); + Tlv124 = new TlvPacket(0x124, new Tlv124()).ToArray(); + } + + [BinaryProperty] public ushort TlvCount { get; set; } + + [BinaryProperty] public byte[] Tlv16E { get; set; } + + [BinaryProperty] public byte[] Tlv147 { get; set; } + + [BinaryProperty] public byte[] Tlv128 { get; set; } + + [BinaryProperty] public byte[] Tlv124 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv145.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv145.cs new file mode 100644 index 0000000..2992a36 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv145.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x145)] +internal class Tlv145 : TlvBody +{ + public Tlv145(BotDeviceInfo deviceInfo) => Guid = deviceInfo.Guid.ToByteArray(); + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv146.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv146.cs new file mode 100644 index 0000000..3b16e1e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv146.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x146)] +internal class Tlv146 : TlvBody +{ + [BinaryProperty] public uint State { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Tag { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Message { get; set; } + + [BinaryProperty] public uint Field0 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv147.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv147.cs new file mode 100644 index 0000000..3b2c9b8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv147.cs @@ -0,0 +1,23 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x147)] +internal class Tlv147 : TlvBody +{ + public Tlv147(BotAppInfo appInfo) + { + AppId = (uint)appInfo.AppId; + PtVersion = appInfo.PtVersion; + PackageName = appInfo.PackageName; + } + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PtVersion { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv161.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv161.cs new file mode 100644 index 0000000..9b5c14e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv161.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x161)] +internal class Tlv161 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv163.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv163.cs new file mode 100644 index 0000000..0c80492 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv163.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X163)] +internal class Tlv163 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv166.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv166.cs new file mode 100644 index 0000000..30bf379 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv166.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x166)] +internal class Tlv166 : TlvBody +{ + [BinaryProperty] public byte ImageType { get; set; } = 5; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv167.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv167.cs new file mode 100644 index 0000000..47f447a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv167.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X167)] +internal class Tlv167 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16A.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16A.cs new file mode 100644 index 0000000..15620f8 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16A.cs @@ -0,0 +1,27 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x16A)] +internal class Tlv16A : TlvBody +{ + /// + /// Tlv0x16A + /// + public Tlv16A(BotKeystore keystore) + { + NoPicSig = keystore.Session.NoPicSig ?? Array.Empty(); + } + + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} + +[Tlv(0x16A, true)] +internal class Tlv16AResponse : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16D.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16D.cs new file mode 100644 index 0000000..1709df7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16D.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X16D)] +internal class Tlv16D : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16E.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16E.cs new file mode 100644 index 0000000..f4678cc --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv16E.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x16E)] +internal class Tlv16E : TlvBody +{ + public Tlv16E(BotDeviceInfo deviceInfo) => DeviceName = deviceInfo.DeviceName; + + [BinaryProperty(Prefix.None)] public string DeviceName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv177.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv177.cs new file mode 100644 index 0000000..9d7b356 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv177.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x177)] +internal class Tlv177 : TlvBody +{ + public Tlv177(BotAppInfo appInfo) => WtLoginSdk = appInfo.WtLoginSdk; + + [BinaryProperty] public byte Field1 { get; set; } = 1; + + [BinaryProperty] public uint BuildTime { get; set; } = 0; // const 0, not sure what this is + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string WtLoginSdk { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv18.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv18.cs new file mode 100644 index 0000000..a0ecb19 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv18.cs @@ -0,0 +1,27 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x018)] +internal class Tlv18 : TlvBody +{ + public Tlv18(BotKeystore keystore) => Uin = keystore.Uin; + + [BinaryProperty] public ushort PingVersion { get; set; } = 0; + + [BinaryProperty] public uint SsoVersion { get; set; } = 0x00000005; + + [BinaryProperty] public uint AppId { get; set; } = 0; // const 0, not sure what this is + + [BinaryProperty] public uint AppClientVersion { get; set; } = 8001; + + [BinaryProperty] public uint Uin { get; set; } + + [BinaryProperty] public ushort Field0 { get; set; } = 0; + + [BinaryProperty] public ushort Field1 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv191.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv191.cs new file mode 100644 index 0000000..857abd0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv191.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x191)] +internal class Tlv191 : TlvBody +{ + [BinaryProperty] public byte K { get; set; } = 0; // originally 0x82 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv305.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv305.cs new file mode 100644 index 0000000..24b62cf --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv305.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X305)] +internal class Tlv305 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] D2Key { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv318.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv318.cs new file mode 100644 index 0000000..ae926cd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv318.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x318)] +internal class Tlv318 : TlvBody +{ + // Just empty + // after discovered, NTQQ has already depreciated this tlv +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv508.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv508.cs new file mode 100644 index 0000000..6912249 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv508.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x508)] +internal class Tlv508 : TlvBody +{ + // 猜不透 算了 +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv510.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv510.cs new file mode 100644 index 0000000..b46969a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv510.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X510)] +internal class Tlv510 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv521.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv521.cs new file mode 100644 index 0000000..8a1bee0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv521.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x521)] +internal class Tlv521 : TlvBody +{ + [BinaryProperty] public uint ProductType { get; set; } = 0x13; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string ProductDesc { get; set; } = "basicim"; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv523.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv523.cs new file mode 100644 index 0000000..9c7405a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv523.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X523)] +internal class Tlv523 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv528.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv528.cs new file mode 100644 index 0000000..fe02270 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv528.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X528)] +internal class Tlv528 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv543.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv543.cs new file mode 100644 index 0000000..6bd7350 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv543.cs @@ -0,0 +1,25 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0x543)] +[ProtoContract] +internal class Tlv543 : TlvBody +{ + [ProtoMember(9)] public Tlv543Layer1 Layer1 { get; set; } +} + +[ProtoContract] +internal class Tlv543Layer1 +{ + [ProtoMember(11)] public Tlv543Layer2 Layer2 { get; set; } +} + +[ProtoContract] +internal class Tlv543Layer2 +{ + [ProtoMember(1)] public string Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv550.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv550.cs new file mode 100644 index 0000000..1fe7313 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/Tlv550.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[Tlv(0X550)] +internal class Tlv550 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode11.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode11.cs new file mode 100644 index 0000000..ebef811 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode11.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x11)] +internal class TlvQrCode11 : TlvBody +{ + public TlvQrCode11(BotKeystore keystore) + { + UnusualSign = keystore.Session.UnusualSign ?? throw new InvalidOperationException(); + } + + [BinaryProperty(Prefix.None)] public byte[] UnusualSign { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode12.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode12.cs new file mode 100644 index 0000000..31e788d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode12.cs @@ -0,0 +1,8 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x012)] +internal class TlvQrCode12 : TlvBody { } \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode15.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode15.cs new file mode 100644 index 0000000..d550117 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode15.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x015)] +internal class TlvQrCode15 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode16.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode16.cs new file mode 100644 index 0000000..ae01002 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode16.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x016)] +internal class Tlv16QrCode : TlvBody +{ + public Tlv16QrCode(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + SubAppId = (uint)appInfo.AppId; + AppIdQrCode = (uint)appInfo.AppIdQrCode; + Guid = deviceInfo.Guid.ToByteArray(); + PackageName = appInfo.PackageName; + PtVersion = appInfo.PtVersion; + PackageName2 = appInfo.PackageName; + } + + [BinaryProperty] public uint Field0 { get; } = 0; // unknown + + [BinaryProperty] public uint SubAppId { get; } + + [BinaryProperty] public uint AppIdQrCode { get; } + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PtVersion { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName2 { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode17.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode17.cs new file mode 100644 index 0000000..9d2d98e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode17.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x017)] +internal class TlvQrCode17 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] QrCode { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode18.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode18.cs new file mode 100644 index 0000000..c00f396 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode18.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x018)] +internal class TlvQrCode18 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] TempPassword { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode19.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode19.cs new file mode 100644 index 0000000..dd9b158 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode19.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x019)] +internal class TlvQrCode19 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1B.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1B.cs new file mode 100644 index 0000000..79c69da --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1B.cs @@ -0,0 +1,25 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x01b)] +internal class TlvQrCode1B : TlvBody +{ + [BinaryProperty] public uint Micro { get; set; } = 0; + + [BinaryProperty] public uint Version { get; set; } = 0; + + [BinaryProperty] public uint Size { get; set; } = 3; + + [BinaryProperty] public uint Margin { get; set; } = 4; + + [BinaryProperty] public uint Dpi { get; set; } = 72; + + [BinaryProperty] public uint EcLevel { get; set; } = 2; + + [BinaryProperty] public uint Hint { get; set; } = 2; + + [BinaryProperty] public ushort Field0 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1C.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1C.cs new file mode 100644 index 0000000..00ebe78 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1C.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x01c)] +internal class TlvQrCode1C : TlvBody +{ + [BinaryProperty] public uint ExpireSec { get; set; } + + [BinaryProperty] public ushort ExpireMin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1D.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1D.cs new file mode 100644 index 0000000..8bb0e65 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1D.cs @@ -0,0 +1,23 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +/// +/// Different from oicq +/// +[TlvQrCode(0x01d)] +internal class TlvQrCode1D : TlvBody +{ + public TlvQrCode1D(BotAppInfo appInfo) => MiscBitmap = (uint)appInfo.MiscBitmap; + + [BinaryProperty] public byte FieldByte { get; set; } = 1; + + [BinaryProperty] public uint MiscBitmap { get; set; } + + [BinaryProperty] public uint Field0 { get; set; } = 0; + + [BinaryProperty] public byte FieldByte0 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1E.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1E.cs new file mode 100644 index 0000000..30a02e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode1E.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x01e)] +internal class TlvQrCode1E : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] TgtgtKey { get; set; } // a1_sig +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode2.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode2.cs new file mode 100644 index 0000000..83ca43b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode2.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x002)] +internal class TlvQrCode2 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode33.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode33.cs new file mode 100644 index 0000000..e8f3a37 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode33.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x033)] +internal class TlvQrCode33 : TlvBody +{ + public TlvQrCode33(BotDeviceInfo deviceInfo) => Guid = deviceInfo.Guid.ToByteArray(); + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode35.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode35.cs new file mode 100644 index 0000000..518660e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode35.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x035)] +internal class TlvQrCode35 : TlvBody +{ + public TlvQrCode35(BotAppInfo appInfo) => PtOsVersion = appInfo.SsoVersion; + + [BinaryProperty] public int PtOsVersion { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode4.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode4.cs new file mode 100644 index 0000000..4b7a1d7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode4.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x004)] +internal class TlvQrCode4 : TlvBody +{ + [BinaryProperty] public ushort Type { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode65.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode65.cs new file mode 100644 index 0000000..4f51982 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode65.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x065)] +internal class TlvQrCode65 : TlvBody +{ + // Just Empty +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode66.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode66.cs new file mode 100644 index 0000000..9492538 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode66.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x066)] +internal class TlvQrCode66 : TlvBody +{ + public TlvQrCode66(BotAppInfo appInfo) => PtOsVersion = appInfo.SsoVersion; + + [BinaryProperty] public int PtOsVersion { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode7.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode7.cs new file mode 100644 index 0000000..4f86fd2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCode7.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x007)] +internal class Tlv7 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeCE.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeCE.cs new file mode 100644 index 0000000..fae5a1c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeCE.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x0CE)] +internal class TlvQrCodeCE : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeD1.cs b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeD1.cs new file mode 100644 index 0000000..b177f09 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Packets/Tlv/TlvQrCodeD1.cs @@ -0,0 +1,36 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using ProtoBuf; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Tlv; + +[TlvQrCode(0x0d1)] +[ProtoContract] +internal class TlvQrCodeD1 : TlvBody +{ + public TlvQrCodeD1(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + Sys = new NTOS + { + Name = deviceInfo.DeviceName, + OS = appInfo.Os, + }; + Type = new byte[] { 0x30, 0x01 }; // actually there is a type ext but i'm too lazy to implement it so i just hardcode it + } + + [ProtoMember(1)] public NTOS Sys { get; set; } + + [ProtoMember(4)] public byte[] Type { get; set; } +} + +[TlvQrCode(0x0d1, true)] +[ProtoContract] +internal class TlvQrCodeD1Resp : TlvBody +{ + [ProtoMember(2)] public string Url { get; set; } + + [ProtoMember(3)] public string QrSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/DeleteFriendService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/DeleteFriendService.cs new file mode 100644 index 0000000..bfa49e2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/DeleteFriendService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(DeleteFriendEvent))] +[Service("OidbSvcTrpcTcp.0x126b_0")] +internal class DeleteFriendService : BaseService +{ + protected override bool Build(DeleteFriendEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, + out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x126B_0 + { + Field1 = new OidbSvcTrpcTcp0x126B_0_Field1 { TargetUid = input.TargetUid, Block = input.Block } + },0x126b, 0, false, false); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out DeleteFriendEvent output, + out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = DeleteFriendEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs new file mode 100644 index 0000000..91ac8ec --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs @@ -0,0 +1,51 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FetchAiCharacterListEvent))] +[Service("OidbSvcTrpcTcp.0x929d_0")] +internal class FetchAiCharacterListService : BaseService +{ + protected override bool Build(FetchAiCharacterListEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x929D_0() + { + Group = input.GroupUin, + ChatType = input.ChatType, + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchAiCharacterListEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + extraEvents = null; + if (payload.ErrorCode == 0) + { + + output = FetchAiCharacterListEvent.Result((int)payload.ErrorCode,payload.Body.Property + .Select(x => new AiCharacterList(x.Type, + x.Vulue.Select(x => new AiCharacter(x.CharacterId, x.CharacterName, x.CharacterVoiceUrl)).ToList()) + ).ToList(),string.Empty); + } + else + { + output = FetchAiCharacterListEvent.Result((int)payload.ErrorCode, null,payload.ErrorMsg); + } + + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchCustomFaceService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchCustomFaceService.cs new file mode 100644 index 0000000..8677a50 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchCustomFaceService.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Action; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FetchCustomFaceEvent))] +[Service("Faceroam.OpReq")] +internal class FetchCustomFaceService : BaseService +{ + protected override bool Build(FetchCustomFaceEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new FaceRoamRequest + { + Comm = new PlatInfo + { + ImPlat = 1, + OsVersion = device.KernelVersion, + QVersion = appInfo.CurrentVersion + }, + SelfUin = keystore.Uin, + SubCmd = 1, + Field6 = 1 + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchCustomFaceEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + var results = payload.UserInfo.FileName.Select(name => $"https://p.qpic.cn/{payload.UserInfo.Bid}/{keystore.Uin}/{name}/0").ToList(); + + output = FetchCustomFaceEvent.Result((int)payload.RetCode, results); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchGroupAtAllRemainService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchGroupAtAllRemainService.cs new file mode 100644 index 0000000..6ddfdfb --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchGroupAtAllRemainService.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FetchGroupAtAllRemainEvent))] +[Service("OidbSvcTrpcTcp.0x8a7_0")] +internal class FetchGroupAtAllRemainService : BaseService +{ + protected override bool Build(FetchGroupAtAllRemainEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x8A7_0 + { + SubCmd = 1, + LimitIntervalTypeForUin = 2, + LimitIntervalTypeForGroup = 1, + Uin = keystore.Uin, + GroupCode = input.GroupUin + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchGroupAtAllRemainEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + output = FetchGroupAtAllRemainEvent.Result((int)packet.ErrorCode, packet.Body.RemainAtAllCountForUin, packet.Body.RemainAtAllCountForGroup); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchMarketFaceKeyService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchMarketFaceKeyService.cs new file mode 100644 index 0000000..ebb6985 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchMarketFaceKeyService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Action; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FetchMarketFaceKeyEvent))] +[Service("BQMallSvc.TabOpReq")] +internal class FetchMarketFaceKeyService : BaseService +{ + protected override bool Build(FetchMarketFaceKeyEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new MarketFaceKeyRequest + { + Field1 = 3, + Info = new MarketFaceInfo + { + FaceIds = input.FaceIds + }, + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out FetchMarketFaceKeyEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + + output = FetchMarketFaceKeyEvent.Result(0, payload.Info.Keys); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchPinsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchPinsService.cs new file mode 100644 index 0000000..a52cc57 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FetchPinsService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FetchPinsEvent))] +[Service("OidbSvcTrpcTcp.0x12b3_0")] +internal class FetchPinsService : BaseService +{ + protected override bool Build(FetchPinsEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(Array.Empty(), 0x12b3, 0); + + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out FetchPinsEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + if (payload.ErrorCode != 0) + { + output = FetchPinsEvent.Result((int)payload.ErrorCode, payload.ErrorMsg); + extraEvents = null; + return true; + } + + var friendUids = payload.Body.Friends?.Select(f => f.Uid).ToList() ?? new List(); + var groupUins = payload.Body.Groups?.Select(f => f.Uin).ToList() ?? new List(); + + output = FetchPinsEvent.Result(friendUids, groupUins); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FriendLikeService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FriendLikeService.cs new file mode 100644 index 0000000..a260b38 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/FriendLikeService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(FriendLikeEvent))] +[Service("OidbSvcTrpcTcp.0x7e5_104")] +internal class FriendLikeService : BaseService +{ + protected override bool Build(FriendLikeEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x7E5_104 + { + TargetUid = input.TargetUid, + Field2 = 71, + Field3 = input.Count + }); + output = packet.Serialize(); + + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FriendLikeEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + output = FriendLikeEvent.Result((int)payload.ErrorCode); + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAddReactionService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAddReactionService.cs new file mode 100644 index 0000000..d704f6d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAddReactionService.cs @@ -0,0 +1,42 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupAddReactionEvent))] +[Service("OidbSvcTrpcTcp.0x9082_1")] +internal class GroupAddReactionService : BaseService +{ + protected override bool Build(GroupAddReactionEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x9082 + { + GroupUin = input.GroupUin, + Sequence = input.Sequence, + Code = input.Code, + Type = input.IsEmoji ? 2u : 1u, + Field6 = false, + Field7 = false + }, 0x9082, 1, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupAddReactionEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupAddReactionEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs new file mode 100644 index 0000000..631c64a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs @@ -0,0 +1,57 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupAiRecordEvent))] +[Service("OidbSvcTrpcTcp.0x929b_0")] +internal class GroupAiRecordService : BaseService +{ + protected override bool Build(GroupAiRecordEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, + out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x929B_0 + { + GroupCode = input.GroupUin, + VoiceId = input.Character, + Text = input.Text, + ChatType = input.ChatType, + ClientMsgInfo = new OidbSvcTrpcTcp0x929B_0ClientMsgInfo + { + MsgRandom = input.ChatId + } + } + ); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupAiRecordEvent output, + out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + extraEvents = null; + if (payload.ErrorCode != 0) + { + output = GroupAiRecordEvent.Result((int)payload.ErrorCode, payload.ErrorMsg); + } + else + { + output = GroupAiRecordEvent.Result(0, payload.Body.MsgInfo); + } + + + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupClockInService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupClockInService.cs new file mode 100644 index 0000000..fa1d6ac --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupClockInService.cs @@ -0,0 +1,63 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupClockInEvent))] +[Service("OidbSvcTrpcTcp.0xeb7_1")] +internal class GroupClockInService : BaseService +{ + protected override bool Build(GroupClockInEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new() + { + Body = new() + { + Uin = keystore.Uin.ToString(), + GroupUin = input.GroupUin.ToString(), + AppVersion = appInfo.CurrentVersion + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out GroupClockInEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + extraEvents = null; + + var payloadResult = payload.Body?.Body?.Result; + if (payload.ErrorCode != 0 || payloadResult == null) + { + output = GroupClockInEvent.Result((int)payload.ErrorCode, new BotGroupClockInResult(false)); + return true; + } + + if (payloadResult.ClockInInfo1 is not { Length: > 1 } || !long.TryParse(payloadResult.ClockInInfo1[1], out var clockInTimestamp)) + clockInTimestamp = 0; + + var result = new BotGroupClockInResult(true) + { + Title = payloadResult.Title ?? string.Empty, + KeepDayText = payloadResult.KeepDayText ?? string.Empty, + GroupRankText = payloadResult.ClockInInfo1 is { Length: > 0 } ? payloadResult.ClockInInfo1[0] : string.Empty, + ClockInUtcTime = DateTime.UnixEpoch + TimeSpan.FromSeconds(clockInTimestamp), + DetailUrl = payloadResult.DetailUrl ?? string.Empty, + }; + output = GroupClockInEvent.Result((int)payload.ErrorCode, result); + + return true; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSCreateFolderService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSCreateFolderService.cs new file mode 100644 index 0000000..da91cab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSCreateFolderService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSCreateFolderEvent))] +[Service("OidbSvcTrpcTcp.0x6d7_0")] +internal class GroupFSCreateFolderService : BaseService +{ + protected override bool Build(GroupFSCreateFolderEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D7_0 + { + Create = new OidbSvcTrpcTcp0x6D7_0Create + { + GroupUin = input.GroupUin, + RootDirectory = "/", + FolderName = input.Name + } + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSCreateFolderEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFSCreateFolderEvent.Result(packet.Body.Create.Retcode, packet.Body.Create.RetMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDeleteFolderService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDeleteFolderService.cs new file mode 100644 index 0000000..00763fa --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDeleteFolderService.cs @@ -0,0 +1,42 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSDeleteFolderEvent))] +[Service("OidbSvcTrpcTcp.0x6d7_1")] +internal class GroupFSDeleteFolderService : BaseService +{ + protected override bool Build(GroupFSDeleteFolderEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D7_1 + { + Delete = new OidbSvcTrpcTcp0x6D7_1Delete + { + GroupUin = input.GroupUin, + FolderId = input.FolderId + } + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSDeleteFolderEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFSDeleteFolderEvent.Result(packet.Body.Delete.Retcode, packet.Body.Delete.RetMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDownloadService.cs new file mode 100644 index 0000000..040d817 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSDownloadService.cs @@ -0,0 +1,47 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x6d6_2")] +internal class GroupFSDownloadService : BaseService +{ + protected override bool Build(GroupFSDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D6 + { + Download = new OidbSvcTrpcTcp0x6D6Download + { + GroupUin = input.GroupUin, + AppId = 7, + BusId = 102, + FileId = input.FileId + } + }, 0x6D6, 2, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSDownloadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var download = packet.Body.Download; + + string url = $"https://{download.DownloadDns}/ftn_handler/{download.DownloadUrl.Hex(true)}/?fname="; + + output = GroupFSDownloadEvent.Result((int)packet.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSMoveService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSMoveService.cs new file mode 100644 index 0000000..c7af45b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSMoveService.cs @@ -0,0 +1,46 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSMoveEvent))] +[Service("OidbSvcTrpcTcp.0x6d6_5")] +internal class GroupFSMoveService : BaseService +{ + protected override bool Build(GroupFSMoveEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D6 + { + Move = new OidbSvcTrpcTcp0x6D6Move + { + GroupUin = input.GroupUin, + AppId = 7, + BusId = 102, + fileId = input.FileId, + ParentDirectory = input.ParentDirectory, + TargetDirectory = input.TargetDirectory + } + }, 0x6D6, 5, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSMoveEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFSMoveEvent.Result(packet.Body.Move.RetCode, packet.Body.Move.ClientWording); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSRenameFolderService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSRenameFolderService.cs new file mode 100644 index 0000000..d2b3cb2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSRenameFolderService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSRenameFolderEvent))] +[Service("OidbSvcTrpcTcp.0x6d7_2")] +internal class GroupFSRenameFolderService : BaseService +{ + protected override bool Build(GroupFSRenameFolderEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D7_2 + { + Rename = new OidbSvcTrpcTcp0x6D7_2Rename + { + GroupUin = input.GroupUin, + FolderId = input.FolderId, + NewFolderName = input.NewFolderName + } + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSRenameFolderEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFSRenameFolderEvent.Result(packet.Body.Rename.Retcode, packet.Body.Rename.RetMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSViewService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSViewService.cs new file mode 100644 index 0000000..4b6ca54 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFSViewService.cs @@ -0,0 +1,105 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFSListEvent))] +[EventSubscribe(typeof(GroupFSCountEvent))] +[EventSubscribe(typeof(GroupFSSpaceEvent))] +[Service("OidbSvcTrpcTcp.0x6d8_1")] +internal class GroupFSViewService : BaseService +{ + protected override bool Build(GroupFSViewEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + object packet = input switch + { + GroupFSListEvent list => new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x6D8 + { + List = new OidbSvcTrpcTcp0x6D8List + { + GroupUin = list.GroupUin, + AppId = 7, + TargetDirectory = list.TargetDirectory, + FileCount = 20, + SortBy = 1, + StartIndex = list.StartIndex, + Field17 = 2, + Field18 = 0 + } + }, 0x6D8, 1, false, true), + GroupFSCountEvent count => new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x6D8 + { + Count = new OidbSvcTrpcTcp0x6D8Count { GroupUin = count.GroupUin, AppId = 7, BusId = 6 } + }, 0x6D8, 2, false, true), + GroupFSSpaceEvent space => new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x6D8 + { + Space = new OidbSvcTrpcTcp0x6D8Space { GroupUin = space.GroupUin, AppId = 7 } + },0x6D8, 3, false, true), + _ => throw new Exception() + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSViewEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + extraEvents = null; + + if (packet.Body.List is { RetCode: 0 }) + { + var items = packet.Body.List.Items ?? new List(); + var fileEntries = items.Select(x => + { + var f = x.FileInfo; + var s = x.FolderInfo; + + IBotFSEntry entry = x.Type switch + { + 1 => new BotFileEntry(f.FileId, f.FileName, f.ParentDirectory, f.FileSize, + DateTime.UnixEpoch.AddSeconds(f.ExpireTime), + DateTime.UnixEpoch.AddSeconds(f.ModifiedTime), f.UploaderUin, + DateTime.UnixEpoch.AddSeconds(f.UploadedTime), f.DownloadedTimes), + 2 => new BotFolderEntry(s.FolderId, s.ParentDirectoryId, s.FolderName, + DateTime.UnixEpoch.AddSeconds(s.CreateTime), + DateTime.UnixEpoch.AddSeconds(s.ModifiedTime), s.CreatorUin, s.TotalFileCount), + _ => throw new Exception("Unrecognized FileSystem Entry Found") + }; + + return entry; + }).ToList(); + output = GroupFSListEvent.Result((int)packet.ErrorCode, fileEntries, packet.Body.List.IsEnd); + return true; + } + + if (packet.Body.Count != null) + { + var count = packet.Body.Count; + output = GroupFSCountEvent.Result((int)packet.ErrorCode, count.FileCount, count.LimitCount, count.IsFull); + return true; + } + if (packet.Body.Space != null) + { + var space = packet.Body.Space; + output = GroupFSSpaceEvent.Result((int)packet.ErrorCode, space.TotalSpace, space.UsedSpace); + return true; + } + + throw new Exception($"Invalid Packet is given to {nameof(GroupFSViewService)}"); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFinishTodoService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFinishTodoService.cs new file mode 100644 index 0000000..48af9de --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupFinishTodoService.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupFinishTodoEvent))] +[Service("OidbSvcTrpcTcp.0xf90_2")] +internal class GroupFinishTodoService : BaseService +{ + protected override bool Build(GroupFinishTodoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xF90 + { + GroupUin = input.GroupUin, + }, 0xF90, 2); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFinishTodoEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFinishTodoEvent.Result((int)packet.ErrorCode, packet.ErrorMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupGetTodoService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupGetTodoService.cs new file mode 100644 index 0000000..70a212a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupGetTodoService.cs @@ -0,0 +1,46 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupGetTodoEvent))] +[Service("OidbSvcTrpcTcp.0xf8e_1")] +internal class GroupGetTodoService : BaseService +{ + protected override bool Build(GroupGetTodoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xF8E_1 + { + GroupUin = input.GroupUin, + }, 0xF8E, 1); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupGetTodoEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = packet.ErrorCode != 0 + ? GroupGetTodoEvent.Result((int)packet.ErrorCode, packet.ErrorMsg) + : GroupGetTodoEvent.Result( + (int)packet.ErrorCode, + packet.ErrorMsg, + packet.Body.Body.GroupUin, + packet.Body.Body.Sequence, + packet.Body.Body.Preview + ); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupInviteService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupInviteService.cs new file mode 100644 index 0000000..6b9abfe --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupInviteService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupInviteEvent))] +[Service("OidbSvcTrpcTcp.0x758_1")] +internal class GroupInviteService : BaseService +{ + protected override bool Build(GroupInviteEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x758_1 + { + GroupUin = input.GroupUin, + UidList = input.InviteUids.Select(x => new OidbSvcTrpcTcp0x758_1Uid + { + InviteUid = x.Key, + SourceGroupUin = x.Value + }).ToList(), + Field10 = 0 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupInviteEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupInviteEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupKickMemberService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupKickMemberService.cs new file mode 100644 index 0000000..fb4486b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupKickMemberService.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupKickMemberEvent))] +[Service("OidbSvcTrpcTcp.0x8a0_1")] +internal class GroupKickMemberService : BaseService +{ + protected override bool Build(GroupKickMemberEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x8A0_1 + { + GroupUin = input.GroupUin, + TargetUid = input.Uid, + RejectAddRequest = input.RejectAddRequest, + Reason = input.Reason + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupKickMemberEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupKickMemberEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupLeaveService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupLeaveService.cs new file mode 100644 index 0000000..a41d771 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupLeaveService.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupLeaveEvent))] +[Service("OidbSvcTrpcTcp.0x1097_1")] +internal class GroupLeaveService : BaseService +{ + protected override bool Build(GroupLeaveEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x1097_1 + { + GroupUin = input.GroupUin + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupLeaveEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupLeaveEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteGlobalService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteGlobalService.cs new file mode 100644 index 0000000..f75c00a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteGlobalService.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using ProtoBuf.Meta; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupMuteGlobalEvent))] +[Service("OidbSvcTrpcTcp.0x89a_0")] +internal class GroupMuteGlobalService : BaseService +{ + private static readonly RuntimeTypeModel Serializer = RuntimeTypeModel.Create(); + + static GroupMuteGlobalService() => Serializer.UseImplicitZeroDefaults = false; // 666 + + protected override bool Build(GroupMuteGlobalEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x89A_0 + { + GroupUin = input.GroupUin, + State = new OidbSvcTrpcTcp0x89A_0State { S = input.IsMute ? uint.MaxValue : 0 } + }); + + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + output = stream.ToArray(); + + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupMuteGlobalEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupMuteGlobalEvent.Result((int)(packet.ErrorCode)); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteMemberService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteMemberService.cs new file mode 100644 index 0000000..9cb6a3d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupMuteMemberService.cs @@ -0,0 +1,44 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupMuteMemberEvent))] +[Service("OidbSvcTrpcTcp.0x1253_1")] +internal class GroupMuteMemberService : BaseService +{ + protected override bool Build(GroupMuteMemberEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x1253_1 + { + GroupUin = input.GroupUin, + Type = 1, + Body = new OidbSvcTrpcTcp0x1253_1Body + { + TargetUid = input.Uid, + Duration = input.Duration + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupMuteMemberEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupMuteMemberEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupReduceReactionService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupReduceReactionService.cs new file mode 100644 index 0000000..2289838 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupReduceReactionService.cs @@ -0,0 +1,42 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupReduceReactionEvent))] +[Service("OidbSvcTrpcTcp.0x9082_2")] +internal class GroupReduceReactionService : BaseService +{ + protected override bool Build(GroupReduceReactionEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x9082 + { + GroupUin = input.GroupUin, + Sequence = input.Sequence, + Code = input.Code, + Type = input.IsEmoji ? 2u : 1u, + Field6 = false, + Field7 = false + }, 0x9082, 2, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupReduceReactionEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupReduceReactionEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemarkService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemarkService.cs new file mode 100644 index 0000000..ff3a854 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemarkService.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupRemarkEvent))] +[Service("OidbSvcTrpcTcp.0xf16_1")] +internal class GroupRemarkService : BaseService +{ + protected override bool Build(GroupRemarkEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xF16_1 + { + Body = new OidbSvcTrpcTcp0xF16_1Body + { + GroupUin = input.GroupUin, + TargetRemark = input.TargetRemark + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupRemarkEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupRemarkEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemoveTodoService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemoveTodoService.cs new file mode 100644 index 0000000..5f9d1fe --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRemoveTodoService.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupRemoveTodoEvent))] +[Service("OidbSvcTrpcTcp.0xf90_3")] +internal class GroupRemoveTodoService : BaseService +{ + protected override bool Build(GroupRemoveTodoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xF90 + { + GroupUin = input.GroupUin, + }, 0xF90, 3); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupRemoveTodoEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupRemoveTodoEvent.Result((int)packet.ErrorCode, packet.ErrorMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRenameService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRenameService.cs new file mode 100644 index 0000000..a2f149f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupRenameService.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupRenameEvent))] +[Service("OidbSvcTrpcTcp.0x89a_15")] +internal class GroupRenameService : BaseService +{ + protected override bool Build(GroupRenameEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x89A_15 + { + GroupUin = input.GroupUin, + Body = new OidbSvcTrpcTcp0x89A_15Body { TargetName = input.TargetName } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupRenameEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupRenameEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetAdminService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetAdminService.cs new file mode 100644 index 0000000..3d9c78e --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetAdminService.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupSetAdminEvent))] +[Service("OidbSvcTrpcTcp.0x1096_1")] +internal class GroupSetAdminService : BaseService +{ + protected override bool Build(GroupSetAdminEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x1096_1 + { + GroupUin = input.GroupUin, + Uid = input.Uid, + IsAdmin = input.IsAdmin + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupSetAdminEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupSetAdminEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBotService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBotService.cs new file mode 100644 index 0000000..53216d5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBotService.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupSetBotEvent))] +[Service("OidbSvcTrpcTcp.0x907d_1")] +internal class GroupSetBotService : BaseService +{ + protected override bool Build(GroupSetBotEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x907D_1 + { + BotId = input.BotId, + type = 2, + On = input.On, + GroupUin = input.GroupUin + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBothdService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBothdService.cs new file mode 100644 index 0000000..f5596d6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetBothdService.cs @@ -0,0 +1,31 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupSetBothdEvent))] +[Service("OidbSvcTrpcTcp.0x112e_1")] +internal class GroupSetBothdService : BaseService +{ + protected override bool Build(GroupSetBothdEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x112E_1 + { + BotId = input.BotId, + Seq = 11111, + B_id = input.Data_1 ?? "", + B_data = input.Data_2 ?? "", + IDD = 0, + GroupUin = input.GroupUin, + GroupType = 1 + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetSpecialTitleService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetSpecialTitleService.cs new file mode 100644 index 0000000..68b9607 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetSpecialTitleService.cs @@ -0,0 +1,44 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupSetSpecialTitleEvent))] +[Service("OidbSvcTrpcTcp.0x8fc_2")] +internal class GroupSetSpecialTitleService : BaseService +{ + protected override bool Build(GroupSetSpecialTitleEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x8FC + { + GroupUin = input.GroupUin, + Body = new OidbSvcTrpcTcp0x8FCBody + { + TargetUid = input.TargetUid, + SpecialTitle = input.Title, + SpecialTitleExpireTime = -1, + UinName = input.Title + } + }, 0x8fc, 2); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupSetSpecialTitleEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupSetSpecialTitleEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetTodoService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetTodoService.cs new file mode 100644 index 0000000..12fd6ae --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupSetTodoService.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupSetTodoEvent))] +[Service("OidbSvcTrpcTcp.0xf90_1")] +internal class GroupSetTodoService : BaseService +{ + protected override bool Build(GroupSetTodoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xF90 + { + GroupUin = input.GroupUin, + Sequence = input.Sequence + }, 0xF90, 1); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupSetTodoEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupSetTodoEvent.Result((int)packet.ErrorCode, packet.ErrorMsg); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupTransferService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupTransferService.cs new file mode 100644 index 0000000..8fbd047 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/GroupTransferService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupTransferEvent))] +[Service("OidbSvcTrpcTcp.0x89e_0")] +internal class GroupTransferService : BaseService +{ + protected override bool Build(GroupTransferEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x89E_0 + { + GroupUin = input.GroupUin, + SourceUid = input.SourceUid, + TargetUid = input.TargetUid + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupTransferEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupTransferEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs new file mode 100644 index 0000000..fadf49a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs @@ -0,0 +1,57 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(ImageOcrEvent))] +[Service("OidbSvcTrpcTcp.0xe07_0")] +internal class ImageOcrService : BaseService +{ + protected override bool Build(ImageOcrEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xE07_0 + { + Version = 1, + Client = 0, + Entrance = 1, + OcrReqBody = new OcrReqBody + { + ImageUrl = input.Url, + OriginMd5 = "", + AfterCompressMd5 = "", + AfterCompressFileSize = "", + AfterCompressWeight = "", + AfterCompressHeight = "", + IsCut = false + } + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageOcrEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var response = new ImageOcrResult( + packet?.Body.OcrRspBody.TextDetections.Select(d => new Common.Entity.TextDetection( + d.DetectedText, + (int)d.Confidence, + d.Polygon.Coordinates.Select(c => new Common.Entity.Coordinate(c.X, c.Y)).ToList() + )).ToList() ?? new List(), + packet?.Body.OcrRspBody.Language ?? string.Empty + ); + output = ImageOcrEvent.Result(packet?.Body.RetCode ?? -1, response); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/JoinEmojiChainService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/JoinEmojiChainService.cs new file mode 100644 index 0000000..4364dcd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/JoinEmojiChainService.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupJoinEmojiChainEvent))] +[EventSubscribe(typeof(FriendJoinEmojiChainEvent))] +[Service("OidbSvcTrpcTcp.0x90ee_1")] +internal class JoinEmojiChainService : BaseService +{ + protected override bool Build(JoinEmojiChainEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x90EE_1 + { + FaceId = input.TargetFaceId, + TargetMsgSeq = input.TargetMessageSeq, + TargetMsgSeq_2 = input.TargetMessageSeq, + Field4 = input.GroupUin == null ? 1 : 2, + TargetGroupId = input.GroupUin, + TargetUid = input.FriendUid, + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out JoinEmojiChainEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + output = JoinEmojiChainEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RenameMemberService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RenameMemberService.cs new file mode 100644 index 0000000..55fbb65 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RenameMemberService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(RenameMemberEvent))] +[Service("OidbSvcTrpcTcp.0x8fc_3")] +internal class RenameMemberService : BaseService +{ + protected override bool Build(RenameMemberEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x8FC + { + GroupUin = input.GroupUin, + Body = new OidbSvcTrpcTcp0x8FCBody + { + TargetUid = input.TargetUid, + TargetName = input.TargetName + } + }, 0x8fc, 3); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RenameMemberEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = RenameMemberEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSearchService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSearchService.cs new file mode 100644 index 0000000..abc83ab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSearchService.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(RequestFriendSearchEvent))] +[Service("OidbSvcTrpcTcp.0x972_6")] +internal class RequestFriendSearchService : BaseService +{ + protected override bool Build(RequestFriendSearchEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x972_6 + { + TargetUin = input.TargetUin.ToString(), + Settings = new OidbSvcTrpcTcp0x972_6Settings + { + Field4 = 25, + Field11 = "", + Setting = "{\"search_by_uid\":true, \"scenario\":\"related_people_and_groups_panel\"}" + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RequestFriendSearchEvent output, out List? extraEvents) + { + output = RequestFriendSearchEvent.Result(0); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendService.cs new file mode 100644 index 0000000..35320c0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendService.cs @@ -0,0 +1,55 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf.Meta; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(RequestFriendEvent))] +[Service("OidbSvcTrpcTcp.0x7c2_5")] +internal class RequestFriendService : BaseService +{ + private static readonly RuntimeTypeModel Serializer; + + static RequestFriendService() + { + Serializer = RuntimeTypeModel.Create(); + Serializer.UseImplicitZeroDefaults = false; + } + + protected override bool Build(RequestFriendEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x7C2_5 + { + SelfUin = keystore.Uin, + TargetUin = input.TargetUin, + Field3 = 1, + Field4 = 1, + Field5 = 0, + Remark = "", + SourceId = 1, + SubSourceId = 3, + Verify = input.Message, + CategoryId = 0, + Answer = input.Question, + Field28 = 1, + Field29 = 1 + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RequestFriendEvent output, out List? extraEvents) + { + output = RequestFriendEvent.Result(0); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSettingService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSettingService.cs new file mode 100644 index 0000000..1673f13 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/RequestFriendSettingService.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(RequestFriendSettingEvent))] +[Service("OidbSvcTrpcTcp.0x7c1_1")] +internal class RequestFriendSettingService : BaseService +{ + protected override bool Build(RequestFriendSettingEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x7C1_1 + { + Field1 = 1, + SelfUin = keystore.Uin, + TargetUin = input.TargetUin, + Field4 = 3999, + Field5 = 0 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RequestFriendSettingEvent output, out List? extraEvents) + { + output = RequestFriendSettingEvent.Result(0); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupFilteredRequestService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupFilteredRequestService.cs new file mode 100644 index 0000000..999709f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupFilteredRequestService.cs @@ -0,0 +1,44 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(SetGroupFilteredRequestEvent))] +[Service("OidbSvcTrpcTcp.0x10c8_2")] +internal class SetGroupFilteredRequestService : BaseService +{ + protected override bool Build(SetGroupFilteredRequestEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x10C8 + { + Accept = Convert.ToUInt32(!input.Accept) + 1, + Body = new OidbSvcTrpcTcp0x10C8Body + { + Sequence = input.Sequence, + EventType = input.Type, + GroupUin = input.GroupUin, + Message = input.Reason + } + }, 0x10c8, 2, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetGroupFilteredRequestEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetGroupFilteredRequestEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupRequestService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupRequestService.cs new file mode 100644 index 0000000..b7b2a66 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetGroupRequestService.cs @@ -0,0 +1,44 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(SetGroupRequestEvent))] +[Service("OidbSvcTrpcTcp.0x10c8_1")] +internal class SetGroupRequestService : BaseService +{ + protected override bool Build(SetGroupRequestEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x10C8 + { + Accept = Convert.ToUInt32(!input.Accept) + 1, + Body = new OidbSvcTrpcTcp0x10C8Body + { + Sequence = input.Sequence, + EventType = input.Type, + GroupUin = input.GroupUin, + Message = input.Reason + } + }, 0x10c8, 1, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetGroupRequestEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetGroupRequestEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinFriendService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinFriendService.cs new file mode 100644 index 0000000..95349a4 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinFriendService.cs @@ -0,0 +1,55 @@ +using System.Buffers.Binary; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(SetPinFriendEvent))] +[Service("OidbSvcTrpcTcp.0x5d6_18")] +internal class SetPinFriendService : BaseService +{ + protected override bool Build(SetPinFriendEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x5D6_18 + { + Field1 = 0, + Info = new OidbSvcTrpcTcp0x5D6_18Info + { + FriendUid = input.Uid, + Field400 = new OidbSvcTrpcTcp0x5D6_18Field4_2_400 + { + Field1 = 13578, + Timestamp = input.IsPin ? GetTimestamp() : Array.Empty() + } + }, + Field3 = 1 + } + ); + + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out SetPinFriendEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetPinFriendEvent.Result((int)payload.ErrorCode, payload.ErrorMsg); + extraEvents = null; + return true; + } + + private static byte[] GetTimestamp() { + byte[] timestamp = new byte[4]; + BinaryPrimitives.WriteInt32BigEndian(timestamp, (int)DateTimeOffset.Now.ToUnixTimeSeconds()); + return timestamp; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinGroupService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinGroupService.cs new file mode 100644 index 0000000..1964b8b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetPinGroupService.cs @@ -0,0 +1,55 @@ +using System.Buffers.Binary; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(SetPinGroupEvent))] +[Service("OidbSvcTrpcTcp.0x5d6_1")] +internal class SetPinGroupService : BaseService +{ + protected override bool Build(SetPinGroupEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x5D6_1 + { + Field1 = 0, + Info = new OidbSvcTrpcTcp0x5D6_1Info + { + GroupUin = input.Uin, + Field400 = new OidbSvcTrpcTcp0x5D6_1Field4_2_400 + { + Field1 = 13569, + Timestamp = input.IsPin ? GetTimestamp() : Array.Empty() + } + }, + Field3 = 11 + } + ); + + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out SetPinGroupEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetPinGroupEvent.Result((int)payload.ErrorCode, payload.ErrorMsg); + extraEvents = null; + return true; + } + + private static byte[] GetTimestamp() { + byte[] timestamp = new byte[4]; + BinaryPrimitives.WriteInt32BigEndian(timestamp, (int)DateTimeOffset.Now.ToUnixTimeSeconds()); + return timestamp; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetStatusService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetStatusService.cs new file mode 100644 index 0000000..0bf9f9f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Action/SetStatusService.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Action; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(SetStatusEvent))] +[EventSubscribe(typeof(SetCustomStatusEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.SetStatus")] +internal class SetStatusService : BaseService +{ + protected override bool Build(SetStatusEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new SetStatus + { + Field1 = 0, + Status = input.Status, + ExtStatus = input.ExtStatus + }; + + if (input is SetCustomStatusEvent custom) + { + packet.CustomExt = new SetStatusCustomExt + { + FaceId = custom.FaceId, + Text = custom.Text, + Field3 = 1 + }; + } + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetStatusEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + extraEvents = null; + output = payload.Message == "set status success" ? SetStatusEvent.Result(0) : SetStatusEvent.Result(-1); + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/BaseService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/BaseService.cs new file mode 100644 index 0000000..ba607ab --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/BaseService.cs @@ -0,0 +1,33 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; + +namespace Lagrange.Core.Internal.Service; + +internal class BaseService : IService where TEvent : ProtocolEvent +{ + protected virtual bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out TEvent output, out List? extraEvents) + { + extraEvents = null; + return (output = null!) != null; + } + + protected virtual bool Build(TEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + extraPackets = null; + return (output = null!) != null; + } + + bool IService.Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ProtocolEvent? output, out List? extraEvents) + { + bool result = Parse(input, keystore, appInfo, device, out var @event, out extraEvents); + output = @event; + return result; + } + + bool IService.Build(ProtocolEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) => + Build((TEvent) input, keystore, appInfo, device, out output, out extraPackets); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/EventSubscribeAttribute.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/EventSubscribeAttribute.cs new file mode 100644 index 0000000..a215a3d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/EventSubscribeAttribute.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Internal.Service; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal class EventSubscribeAttribute : Attribute +{ + public Type EventType { get; } + + public EventSubscribeAttribute(Type eventType) + { + EventType = eventType; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/IService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/IService.cs new file mode 100644 index 0000000..21adf4c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/IService.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; + +namespace Lagrange.Core.Internal.Service; + +internal interface IService +{ + public bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ProtocolEvent? output, out List? extraEvents); + + public bool Build(ProtocolEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets); +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/EasyLoginService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/EasyLoginService.cs new file mode 100644 index 0000000..999a7e2 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/EasyLoginService.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.NTLogin; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Utility.Crypto; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(EasyLoginEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginEasyLogin")] +internal class EasyLoginService : BaseService +{ + protected override bool Build(EasyLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + if (keystore.Session.TempPassword == null) throw new InvalidOperationException("TempPassword is null"); + + output = SsoNTLoginCommon.BuildNTLoginPacket(keystore, appInfo, device, keystore.Session.TempPassword); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out EasyLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + var encrypted = Serializer.Deserialize(input); + + if (encrypted.GcmCalc != null) + { + var decrypted = AesGcmImpl.Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()); + var body = response.Body; + + if (response.Header?.Error != null || body?.Credentials == null) + { + output = EasyLoginEvent.Result((int)(response.Header?.Error?.ErrorCode ?? 1)); + + keystore.Session.UnusualSign = body?.Unusual?.Sig; + keystore.Session.UnusualCookies = response.Header?.Cookie?.Cookie; + } + else + { + keystore.Session.Tgt = body.Credentials.Tgt; + keystore.Session.D2 = body.Credentials.D2; + keystore.Session.D2Key = body.Credentials.D2Key; + keystore.Session.TempPassword = body.Credentials.TempPassword; + keystore.Session.SessionDate = DateTime.Now; + + output = EasyLoginEvent.Result(0); + } + } + else + { + output = EasyLoginEvent.Result(1); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/KeyExchangeService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/KeyExchangeService.cs new file mode 100644 index 0000000..d5c98e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/KeyExchangeService.cs @@ -0,0 +1,79 @@ +using System.Security.Cryptography; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.Ecdh; +using Lagrange.Core.Internal.Packets.Login.Ecdh.Plain; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(KeyExchangeEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoKeyExchange")] +internal class KeyExchangeService : BaseService +{ + private const string GcmCalc2Key = "e2733bf403149913cbf80c7a95168bd4ca6935ee53cd39764beebe2e007e3aee"; + + protected override bool Build(KeyExchangeEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + output = BuildPacket(keystore, device).Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out KeyExchangeEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize(input); + + var shareKey = keystore.PrimeImpl.GenerateShared(response.PublicKey, false); + var gcmDecrypted = AesGcmImpl.Decrypt(response.GcmEncrypted, shareKey); + var decrypted = Serializer.Deserialize(gcmDecrypted.AsSpan()); + + keystore.Session.ExchangeKey = decrypted.GcmKey; + keystore.Session.KeySign = decrypted.Sign; + output = KeyExchangeEvent.Result(); + + extraEvents = null; + return true; + } + + private static SsoKeyExchange BuildPacket(BotKeystore keystore, BotDeviceInfo deviceInfo) + { + using var stream = new MemoryStream(); + var plain1 = new SsoKeyExchangePlain + { + Uin = keystore.Uin.ToString(), + Guid = deviceInfo.Guid.ToByteArray() + }; + Serializer.Serialize(stream, plain1); + var gcmCalc1 = AesGcmImpl.Encrypt(stream.ToArray(), keystore.PrimeImpl.ShareKey); + + var timestamp = (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var plain2 = new SsoKeyExchangePlain2 + { + PublicKey = keystore.PrimeImpl.GetPublicKey(false), + Type = 1, + EncryptedGcm = gcmCalc1, + Const = 0, + Timestamp = timestamp + }; + var stream2 = BinarySerializer.Serialize(plain2); + using var sha256 = SHA256.Create(); + var hash = sha256.ComputeHash(stream2.ToArray()); + var gcmCalc2 = AesGcmImpl.Encrypt(hash, GcmCalc2Key.UnHex()); + + return new SsoKeyExchange + { + PubKey = keystore.PrimeImpl.GetPublicKey(false), + Type = 1, + GcmCalc1 = gcmCalc1, + Timestamp = timestamp, + GcmCalc2 = gcmCalc2 + }; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/LoginService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/LoginService.cs new file mode 100644 index 0000000..799ce32 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/LoginService.cs @@ -0,0 +1,63 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Tlv; +using Lagrange.Core.Utility.Binary; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(LoginEvent))] +[Service("wtlogin.login")] +internal class LoginService : BaseService +{ + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out LoginEvent output, out List? extraEvents) + { + var payload = BitConverter.GetBytes(input.Length, false).Concat(input.ToArray()).ToArray(); // TODO: 这啥玩意啊 + var tlvs = Packets.Login.WtLogin.Entity.Login.Deserialize(new BinaryPacket(payload), keystore, out var state); + + if (state == Packets.Login.WtLogin.Entity.Login.State.Success) + { + var tlv11A = (Tlv11A)tlvs[0x11A]; + var tlv305 = (Tlv305)tlvs[0x305]; + var tlv543 = (Tlv543)tlvs[0x543]; + var tlv10A = (Tlv10A)tlvs[0x10A]; + var tlv143 = (Tlv143)tlvs[0x143]; + var tlv106 = (Tlv106Response)tlvs[0x106]; + + keystore.Session.D2Key = tlv305.D2Key; + keystore.Uid = tlv543.Layer1.Layer2.Uid; + keystore.Session.Tgt = tlv10A.Tgt; + keystore.Session.D2 = tlv143.D2; + keystore.Session.TempPassword = tlv106.Temp; + keystore.Session.SessionDate = DateTime.Now; + + output = LoginEvent.Result((int)state, tlv11A.Age, tlv11A.Gender, tlv11A.Nickname); + extraEvents = null; + return true; + } + + if (tlvs.TryGetValue(0x146, out var tlv)) + { + var tlv146 = (Tlv146)tlv; + output = LoginEvent.Result((int)state, tlv146.Tag, tlv146.Message); + extraEvents = null; + return true; + } + + output = LoginEvent.Result((int)state); + extraEvents = null; + return true; + } + + protected override bool Build(LoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new Packets.Login.WtLogin.Entity.Login(keystore, appInfo, device); + output = packet.ConstructPacket().ToArray(); + + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/NewDeviceLoginService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/NewDeviceLoginService.cs new file mode 100644 index 0000000..7c1b400 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/NewDeviceLoginService.cs @@ -0,0 +1,62 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.NTLogin; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Utility.Crypto; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(NewDeviceLoginEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLoginNewDevice")] +internal class NewDeviceLoginService : BaseService +{ + protected override bool Build(NewDeviceLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (keystore.Session.TempPassword == null) throw new InvalidOperationException("TempPassword is null"); + + output = SsoNTLoginCommon.BuildNTLoginPacket(keystore, appInfo, device, keystore.Session.TempPassword); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out NewDeviceLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + + var encrypted = Serializer.Deserialize(input); + + if (encrypted.GcmCalc != null) + { + var decrypted = AesGcmImpl.Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()); + var body = response.Body; + + if (response.Header?.Error != null || body is not { Credentials: not null }) + { + output = NewDeviceLoginEvent.Result((int)(response.Header?.Error?.ErrorCode ?? 1)); + } + else + { + keystore.Session.Tgt = body.Credentials.Tgt; + keystore.Session.D2 = body.Credentials.D2; + keystore.Session.D2Key = body.Credentials.D2Key; + keystore.Session.TempPassword = body.Credentials.TempPassword; + keystore.Session.SessionDate = DateTime.Now; + + output = NewDeviceLoginEvent.Result(0); + } + } + else + { + output = NewDeviceLoginEvent.Result(1); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/PasswordLoginService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/PasswordLoginService.cs new file mode 100644 index 0000000..5c2116a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/PasswordLoginService.cs @@ -0,0 +1,85 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.NTLogin; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; +using ProtoBuf; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(PasswordLoginEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin")] +internal class PasswordLoginService : BaseService +{ + protected override bool Build(PasswordLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var plainBody = new SsoNTLoginPasswordLogin + { + Random = (uint)Random.Shared.Next(), + AppId = (uint)appInfo.AppId, + Uin = keystore.Uin, + Timestamp = (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), + PasswordMd5 = keystore.PasswordMd5.UnHex(), + RandomBytes = ByteGen.GenRandomBytes(16), + Guid = device.Guid.ToByteArray(), + UinString = keystore.Uin.ToString() + }; + var plainBytes = BinarySerializer.Serialize(plainBody); + var plainKey = keystore.PasswordMd5.UnHex().Concat(new byte[4]).Concat(BitConverter.GetBytes(keystore.Uin, false)).ToArray().Md5().UnHex(); + var encryptedPlain = keystore.TeaImpl.Encrypt(plainBytes.ToArray(), plainKey); // TODO: Investigate key + + output = SsoNTLoginCommon.BuildNTLoginPacket(keystore, appInfo, device, encryptedPlain); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out PasswordLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + var encrypted = Serializer.Deserialize(input); + + if (encrypted.GcmCalc != null) + { + var decrypted = AesGcmImpl.Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()); + var body = response.Body; + + if (response.Header?.Error != null || body?.Credentials == null) + { + keystore.Session.UnusualSign = body?.Unusual?.Sig; + keystore.Session.UnusualCookies = response.Header?.Cookie?.Cookie; + keystore.Session.CaptchaUrl = body?.Captcha?.Url; + keystore.Session.NewDeviceVerifyUrl = response.Header?.Error?.NewDeviceVerifyUrl; + + string? tag = response.Header?.Error?.Tag; + string? message = response.Header?.Error?.Message; + output = PasswordLoginEvent.Result((int)(response.Header?.Error?.ErrorCode ?? 1), tag, message); + } + else + { + keystore.Session.Tgt = body.Credentials.Tgt; + keystore.Session.D2 = body.Credentials.D2; + keystore.Session.D2Key = body.Credentials.D2Key; + keystore.Session.TempPassword = body.Credentials.TempPassword; + keystore.Session.SessionDate = DateTime.Now; + + output = PasswordLoginEvent.Result(0); + } + } + else + { + output = PasswordLoginEvent.Result(1); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/TransEmpService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/TransEmpService.cs new file mode 100644 index 0000000..70352ce --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/TransEmpService.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.WtLogin.Entity; +using Lagrange.Core.Internal.Packets.Tlv; +using Lagrange.Core.Utility.Binary; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(TransEmpEvent))] +[Service("wtlogin.trans_emp")] +internal class TransEmpService : BaseService +{ + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out TransEmpEvent output, out List? extraEvents) + { + var payload = BitConverter.GetBytes(input.Length, false).Concat(input.ToArray()).ToArray(); // 这写的啥 + var packet = TransEmp.DeserializeBody(keystore, new BinaryPacket(payload), out ushort command); + + if (command == 0x31) + { + var tlvs = TransEmp31.Deserialize(packet, keystore, out var signature); + + var qrCode = ((TlvQrCode17)tlvs[0x17]).QrCode; + uint expiration = ((TlvQrCode1C)tlvs[0x01C]).ExpireSec; + string url = ((TlvQrCodeD1Resp)tlvs[0x0D1]).Url; + string qrSig = ((TlvQrCodeD1Resp)tlvs[0x0D1]).QrSig; + + output = TransEmpEvent.Result(qrCode, expiration, url, qrSig, signature); + } + else + { + var tlvs = TransEmp12.Deserialize(packet, out var state); + + if (state == TransEmp12.State.Confirmed) + { + var tgtgtKey = ((TlvQrCode1E)tlvs[0x1E]).TgtgtKey; + var tempPassword = ((TlvQrCode18)tlvs[0x18]).TempPassword; + var noPicSig = ((TlvQrCode19)tlvs[0x19]).NoPicSig; + + output = TransEmpEvent.Result(state, tgtgtKey, tempPassword, noPicSig); + } + else + { + output = TransEmpEvent.Result(state, null, null, null); + } + } + + extraEvents = null; + return true; + } + + protected override bool Build(TransEmpEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + output = input.EventState == TransEmpEvent.State.FetchQrCode + ? new TransEmp31(keystore, appInfo, device).ConstructPacket().ToArray() + : new TransEmp12(keystore, appInfo, device).ConstructPacket().ToArray(); + + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Login/UnusualEasyLoginService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/UnusualEasyLoginService.cs new file mode 100644 index 0000000..42ce2d3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Login/UnusualEasyLoginService.cs @@ -0,0 +1,62 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Login; +using Lagrange.Core.Internal.Packets.Login.NTLogin; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Utility.Crypto; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Login; + +[EventSubscribe(typeof(UnusualEasyLoginEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginEasyLoginUnusualDevice")] +internal class UnusualEasyLoginService : BaseService +{ + protected override bool Build(UnusualEasyLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (keystore.Session.TempPassword == null) throw new InvalidOperationException("TempPassword is null"); + + output = SsoNTLoginCommon.BuildNTLoginPacket(keystore, appInfo, device, keystore.Session.TempPassword); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out UnusualEasyLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + + var encrypted = Serializer.Deserialize(input); + + if (encrypted.GcmCalc != null) + { + var decrypted = AesGcmImpl.Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()); + var body = response.Body; + + if (response.Header?.Error != null || body is not { Credentials: not null }) + { + output = UnusualEasyLoginEvent.Result((int)(response.Header?.Error?.ErrorCode ?? 1)); + } + else + { + keystore.Session.Tgt = body.Credentials.Tgt; + keystore.Session.D2 = body.Credentials.D2; + keystore.Session.D2Key = body.Credentials.D2Key; + keystore.Session.TempPassword = body.Credentials.TempPassword; + keystore.Session.SessionDate = DateTime.Now; + + output = UnusualEasyLoginEvent.Result(0); + } + } + else + { + output = UnusualEasyLoginEvent.Result(1); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileDownloadService.cs new file mode 100644 index 0000000..0b2598b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileDownloadService.cs @@ -0,0 +1,53 @@ +using System.Text; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(FileDownloadEvent))] +[Service("OidbSvcTrpcTcp.0xe37_1200")] +internal class FileDownloadService : BaseService +{ + protected override bool Build(FileDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.FileUuid == null || input.FileHash == null) throw new ArgumentNullException(); + if (input.SenderUid == null || input.ReceiverUid == null) throw new ArgumentNullException(); + + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xE37_1200 + { + Body = new OidbSvcTrpcTcp0xE37_1200Body + { + ReceiverUid = input.ReceiverUid, + FileUuid = input.FileUuid, + FileHash = input.FileHash, + T2 = 0 + }, + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FileDownloadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + var urlBuilder = new StringBuilder() + .Append("http://") + .Append(packet.Body.Body.Result.Server).Append(':').Append(packet.Body.Body.Result.Port) + .Append(packet.Body.Body.Result.Url).Append("&isthumb=0"); + + output = FileDownloadEvent.Result((int)packet.ErrorCode, urlBuilder.ToString()); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileUploadService.cs new file mode 100644 index 0000000..543a503 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/FileUploadService.cs @@ -0,0 +1,57 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(FileUploadEvent))] +[Service("OidbSvcTrpcTcp.0xe37_1700")] +internal class FileUploadService : BaseService +{ + protected override bool Build(FileUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + if (input.Entity.FileStream is null) throw new Exception(); + + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xE37_1700 + { + Command = 1700, + Seq = 0, + Upload = new ApplyUploadReqV3 + { + SenderUid = keystore.Uid ?? "", + ReceiverUid = input.TargetUid, + FileSize = (uint)input.Entity.FileStream.Length, + FileName = input.Entity.FileName, + Md510MCheckSum = input.Entity.FileStream.Md5(0, 10 * 1024 * 1024).UnHex(), + Sha1CheckSum = input.Entity.FileSha1, + LocalPath = "/", + Md5CheckSum = input.Entity.FileMd5, + Sha3CheckSum = Array.Empty() + }, + BusinessId = 3, + ClientType = 1, + FlagSupportMediaPlatform = 1 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FileUploadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var upload = payload.Body.Upload; + + output = FileUploadEvent.Result((int)payload.ErrorCode, upload.BoolFileExist, upload.Uuid, upload.MediaPlatformUploadKey, upload.UploadIp, upload.UploadPort, upload.FileAddon); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetC2cMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetC2cMessageService.cs new file mode 100644 index 0000000..dce00a6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetC2cMessageService.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GetC2cMessageEvent))] +[Service("trpc.msg.register_proxy.RegisterProxy.SsoGetC2cMsg")] +internal class GetC2cMessageService : BaseService +{ + protected override bool Build(GetC2cMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new SsoGetC2cMsg + { + FriendUid = input.FriendUid, + StartSequence = input.StartSequence, + EndSequence = input.EndSequence + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GetC2cMessageEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + var chains = payload.Messages?.Select(x => MessagePacker.Parse(x)).ToList() ?? new(); + + output = GetC2cMessageEvent.Result(chains); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetGroupMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetGroupMessageService.cs new file mode 100644 index 0000000..4fb4365 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetGroupMessageService.cs @@ -0,0 +1,44 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GetGroupMessageEvent))] +[Service("trpc.msg.register_proxy.RegisterProxy.SsoGetGroupMsg")] +internal class GetGroupMessageService : BaseService +{ + protected override bool Build(GetGroupMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new SsoGetGroupMsg + { + Info = new SsoGetGroupMsgInfo + { + GroupUin = input.GroupUin, + StartSequence = input.StartSequence, + EndSequence = input.EndSequence + }, + Direction = true + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GetGroupMessageEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + var chains = payload.Body.Messages.Select(x => MessagePacker.Parse(x)).ToList(); + + output = GetGroupMessageEvent.Result(0, chains); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetLatestGroupMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetLatestGroupMessageService.cs new file mode 100644 index 0000000..aab1ebd --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetLatestGroupMessageService.cs @@ -0,0 +1,80 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GetGroupInfoEvent))] +[Service("OidbSvcTrpcTcp.0x88d_0")] +internal class GetGroupInfoService : BaseService +{ + protected override bool Build(GetGroupInfoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x88D_0 + { + Field1 = 537099973, + Config = new OidbSvcTrpcTcp0x88D_0Config + { + Uin = input.Uin, + Flags = new OidbSvcTrpcTcp0x88D_0Flags + { + OwnerUid = true, + CreateTime = true, + MaxMemberCount = true, + MemberCount = true, + Level = true, + Name = "", + NoticePreview = "", + Uin = true, + LastSequence = true, + LastMessageTime = true, + Question = true, + Answer = "", + MaxAdminCount = "", + } + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GetGroupInfoEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + if (payload.ErrorCode == 0) + { + output = GetGroupInfoEvent.Result(0, null, new BotGroupInfo + { + OwnerUid = payload.Body.GroupInfo.Results.OwnerUid, + CreateTime = payload.Body.GroupInfo.Results.CreateTime, + MaxMemberCount = payload.Body.GroupInfo.Results.MaxMemberCount, + MemberCount = payload.Body.GroupInfo.Results.MemberCount, + Level = payload.Body.GroupInfo.Results.Level, + Name = payload.Body.GroupInfo.Results.Name, + NoticePreview = payload.Body.GroupInfo.Results.NoticePreview, + Uin = payload.Body.GroupInfo.Results.Uin, + LastSequence = payload.Body.GroupInfo.Results.LastSequence, + LastMessageTime = payload.Body.GroupInfo.Results.LastMessageTime, + Question = payload.Body.GroupInfo.Results.Question, + Answer = payload.Body.GroupInfo.Results.Answer, + MaxAdminCount = payload.Body.GroupInfo.Results.MaxAdminCount, + }); + } + else + { + output = GetGroupInfoEvent.Result((int)payload.ErrorCode, payload.ErrorMsg, new()); + } + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetRoamMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetRoamMessageService.cs new file mode 100644 index 0000000..67d8683 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GetRoamMessageService.cs @@ -0,0 +1,42 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GetRoamMessageEvent))] +[Service("trpc.msg.register_proxy.RegisterProxy.SsoGetRoamMsg")] +internal class GetRoamMessageService : BaseService +{ + protected override bool Build(GetRoamMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new SsoGetRoamMsg + { + FriendUid = input.FriendUid, + Time = input.Time, + Random = 0, + Count = input.Count, + Direction = true + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GetRoamMessageEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize(input); + var chains = payload.Messages.Select(x => MessagePacker.Parse(x)).ToList(); + + extraEvents = null; + output = GetRoamMessageEvent.Result(0, chains); + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSDeleteService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSDeleteService.cs new file mode 100644 index 0000000..b3ff6df --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSDeleteService.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[Service("OidbSvcTrpcTcp.0x6d6_3")] +[EventSubscribe(typeof(GroupFSDeleteEvent))] +internal class GroupFSDeleteService : BaseService +{ + protected override bool Build(GroupFSDeleteEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D6 + { + Delete = new OidbSvcTrpcTcp0x6D6Delete + { + GroupUin = input.GroupUin, + BusId = 102, + FileId = input.FileId + } + }, 0x6D6, 3, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSDeleteEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = GroupFSDeleteEvent.Result(packet.Body.Delete.RetCode, packet.Body.Delete.ClientWording); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSUploadService.cs new file mode 100644 index 0000000..a4a8841 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupFSUploadService.cs @@ -0,0 +1,55 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GroupFSUploadEvent))] +[Service("OidbSvcTrpcTcp.0x6d6_0")] +internal class GroupFSUploadService : BaseService +{ + protected override bool Build(GroupFSUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.FileStream is null) throw new Exception(); + + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D6 + { + File = new OidbSvcTrpcTcp0x6D6Upload + { + GroupUin = input.GroupUin, + AppId = 7, + BusId = 102, + Entrance = 6, + TargetDirectory = input.TargetDirectory, + FileName = input.Entity.FileName, + LocalDirectory = $"/{input.Entity.FileName}", + FileSize = input.Entity.FileSize, + FileSha1 = input.Entity.FileStream.Sha1().UnHex(), + FileSha3 = Array.Empty(), + FileMd5 = input.Entity.FileMd5, + Field15 = true + } + }, 0x6D6, 0, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupFSUploadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var upload = payload.Body.Upload; + + output = GroupFSUploadEvent.Result(upload.RetCode, upload.BoolFileExist, upload.FileId, upload.FileKey, upload.CheckKey, upload.UploadIp, upload.UploadPort); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupSendFileService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupSendFileService.cs new file mode 100644 index 0000000..00edf52 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/GroupSendFileService.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(GroupSendFileEvent))] +[Service("OidbSvcTrpcTcp.0x6d9_4")] +internal class GroupSendFileService : BaseService +{ + protected override bool Build(GroupSendFileEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x6D9_4 + { + Body = new OidbSvcTrpcTcp0x6D9_4Body + { + GroupUin = input.GroupUin, + Type = 2, + Info = new OidbSvcTrpcTcp0x6D9_4Info + { + BusiType = 102, + FileId = input.FileKey, + Field3 = (uint)Random.Shared.Next(), + Field5 = true + } + } + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupSendFileEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = GroupSendFileEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageDownloadService.cs new file mode 100644 index 0000000..05e826c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageDownloadService.cs @@ -0,0 +1,71 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(ImageDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x11c5_200")] +internal class ImageDownloadService : BaseService +{ + protected override bool Build(ImageDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 200 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 1, + SceneType = 1, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = input.SelfUid + } + }, + Client = new ClientMeta { AgentType = 2 } + }, + Download = new DownloadReq + { + Node = input.Node, + Download = new DownloadExt + { + Video = new VideoDownloadExt + { + BusiType = 0, + SceneType = 0 + } + } + } + }, 0x11c5, 200, false, true); + + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageDownloadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var body = payload.Body.Download; + string url = $"https://{body.Info.Domain}{body.Info.UrlPath}{body.RKeyParam}"; + + output = ImageDownloadEvent.Result((int)payload.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupDownloadService.cs new file mode 100644 index 0000000..739f18a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupDownloadService.cs @@ -0,0 +1,67 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(ImageGroupDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x11c4_200")] +internal class ImageGroupDownloadService : BaseService +{ + protected override bool Build(ImageGroupDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 200 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 1, + SceneType = 2, + Group = new GroupInfo { GroupUin = input.GroupUin } + }, + Client = new ClientMeta { AgentType = 2 } + }, + Download = new DownloadReq + { + Node = input.Node, + Download = new DownloadExt + { + Video = new VideoDownloadExt + { + BusiType = 0, + SceneType = 0 + } + } + } + }, 0x11c4, 200, false, true); + + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageGroupDownloadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var body = payload.Body.Download; + string url = $"https://{body.Info.Domain}{body.Info.UrlPath}{body.RKeyParam}"; + + output = ImageGroupDownloadEvent.Result((int)payload.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupUploadService.cs new file mode 100644 index 0000000..141557f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageGroupUploadService.cs @@ -0,0 +1,127 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; +using GroupInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.GroupInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(ImageGroupUploadEvent))] +[Service("OidbSvcTrpcTcp.0x11c4_100")] +internal class ImageGroupUploadService : BaseService +{ + protected override bool Build(ImageGroupUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.ImageStream is null) throw new Exception(); + + string md5 = input.Entity.ImageStream.Value.Md5(true); + string sha1 = input.Entity.ImageStream.Value.Sha1(true); + + var buffer = new byte[1024]; // parse image header + int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan()); + var type = ImageResolver.Resolve(buffer, out var size); + string imageExt = type switch + { + ImageFormat.Jpeg => ".jpg", + ImageFormat.Png => ".png", + ImageFormat.Gif => ".gif", + ImageFormat.Webp => ".webp", + ImageFormat.Bmp => ".bmp", + ImageFormat.Tiff => ".tiff", + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + input.Entity.ImageStream.Value.Position = 0; + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 1, + SceneType = 2, + Group = new GroupInfo { GroupUin = input.GroupUin } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.ImageStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = md5 + imageExt, + Type = new FileType + { + Type = 1, + PicFormat = (uint)type, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = (uint)size.X, + Height = (uint)size.Y, + Time = 0, + Original = 1 + }, + SubFileType = 0 + } + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 2, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo + { + TextSummary = input.Entity.Summary!, + BytesPbReserveTroop = "0800180020004a00500062009201009a0100aa010c080012001800200028003a00".UnHex() + }, + Video = new VideoExtBizInfo { BytesPbReserve = Array.Empty() }, + Ptt = new PttExtBizInfo + { + BytesReserve = Array.Empty(), + BytesPbReserve = Array.Empty(), + BytesGeneralFlags = Array.Empty() + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x11c4, 100, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageGroupUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(upload.CompatQMsg.AsSpan()); + + output = ImageGroupUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageUploadService.cs new file mode 100644 index 0000000..d5ae182 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/ImageUploadService.cs @@ -0,0 +1,132 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(ImageUploadEvent))] +[Service("OidbSvcTrpcTcp.0x11c5_100")] +internal class ImageUploadService : BaseService +{ + protected override bool Build(ImageUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.ImageStream is null) throw new Exception(); + + string md5 = input.Entity.ImageStream.Value.Md5(true); + string sha1 = input.Entity.ImageStream.Value.Sha1(true); + + var buffer = new byte[1024]; // parse image header + int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan()); + var type = ImageResolver.Resolve(buffer, out var size); + string imageExt = type switch + { + ImageFormat.Jpeg => ".jpg", + ImageFormat.Png => ".png", + ImageFormat.Gif => ".gif", + ImageFormat.Webp => ".webp", + ImageFormat.Bmp => ".bmp", + ImageFormat.Tiff => ".tiff", + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + input.Entity.ImageStream.Value.Position = 0; + + string uid = (string.IsNullOrEmpty(input.TargetUid) ? keystore.Uid : input.TargetUid) ?? ""; + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 1, + SceneType = 1, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = uid + } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.ImageStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = md5 + imageExt, + Type = new FileType + { + Type = 1, + PicFormat = (uint)type, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = (uint)size.X, + Height = (uint)size.Y, + Time = 0, + Original = 1 + }, + SubFileType = 0 + } + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 1u, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo + { + TextSummary = input.Entity.Summary!, + BytesPbReserveC2c = "0800180020004200500062009201009a0100a2010c080012001800200028003a00".UnHex() + }, + Video = new VideoExtBizInfo { BytesPbReserve = Array.Empty() }, + Ptt = new PttExtBizInfo + { + BytesReserve = Array.Empty(), + BytesPbReserve = Array.Empty(), + BytesGeneralFlags = Array.Empty() + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x11c5, 100, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(upload.CompatQMsg.AsSpan()); + + output = ImageUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MarkReadedService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MarkReadedService.cs new file mode 100644 index 0000000..a232c5c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MarkReadedService.cs @@ -0,0 +1,45 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(MarkReadedEvent))] +[Service("trpc.msg.msg_svc.MsgService.SsoReadedReport")] +internal class MarkReadedService : BaseService +{ + protected override bool Build(MarkReadedEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = input.TargetUid == null ? new SsoReadedReport + { + Group = new SsoReadedReportGroup + { + GroupUin = input.GroupUin, + StartSequence = input.StartSequence + } + } : new SsoReadedReport + { + C2C = new SsoReadedReportC2C + { + TargetUid = input.TargetUid, + Time = input.Time, + StartSequence = input.StartSequence + } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out MarkReadedEvent output, out List? extraEvents) + { + output = MarkReadedEvent.Result(0); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgDownloadService.cs new file mode 100644 index 0000000..2d2ab6c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgDownloadService.cs @@ -0,0 +1,54 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Binary.Compression; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(MultiMsgDownloadEvent))] +[Service("trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg")] +internal class MultiMsgDownloadService : BaseService +{ + protected override bool Build(MultiMsgDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new RecvLongMsgReq + { + Info = new RecvLongMsgInfo + { + Uid = new LongMsgUid { Uid = input.Uid }, + ResId = input.ResId, + Acquire = true + }, + Settings = new LongMsgSettings + { + Field1 = 2, + Field2 = 0, + Field3 = 0, + Field4 = 0 + } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out MultiMsgDownloadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize(input); + var inflate = GZip.Inflate(packet.Result.Payload); + var result = Serializer.Deserialize(inflate.AsSpan()); + + var main = result.Action.First(a => a.ActionCommand == "MultiMsg"); + var chains = main.ActionData.MsgBody.Select(x => MessagePacker.Parse(x, true)).ToList(); + output = MultiMsgDownloadEvent.Result(0, chains); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgUploadService.cs new file mode 100644 index 0000000..a2ca00f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/MultiMsgUploadService.cs @@ -0,0 +1,69 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Binary.Compression; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(MultiMsgUploadEvent))] +[Service("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg")] +internal class MultiMsgUploadService : BaseService +{ + protected override bool Build(MultiMsgUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Chains == null) throw new ArgumentNullException(nameof(input.Chains)); + + var msgBody = input.Chains.Select(x => MessagePacker.BuildFake(x, keystore.Uid ?? "")).ToList(); + var longMsgResult = new LongMsgResult + { + Action = new List { + new() + { + ActionCommand = "MultiMsg", + ActionData = new LongMsgContent { MsgBody = msgBody } + } + } + }; + + using var msgStream = new MemoryStream(); + Serializer.Serialize(msgStream, longMsgResult); + var deflate = GZip.Deflate(msgStream.ToArray()); + + var packet = new SendLongMsgReq + { + Info = new SendLongMsgInfo + { + Type = input.GroupUin == null ? 1u : 3u, + Uid = new LongMsgUid { Uid = input.GroupUin?.ToString() ?? keystore.Uid }, + GroupUin = input.GroupUin, + Payload = deflate + }, + Settings = new LongMsgSettings + { + Field1 = 4, + Field2 = 1, + Field3 = 7, + Field4 = 0 + } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out MultiMsgUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize(input); + + output = MultiMsgUploadEvent.Result(0, packet.Result.ResId); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PokeService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PokeService.cs new file mode 100644 index 0000000..bb46585 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PokeService.cs @@ -0,0 +1,59 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(FriendPokeEvent))] +[EventSubscribe(typeof(GroupPokeEvent))] +[Service("OidbSvcTrpcTcp.0xed3_1")] +internal class PokeService : BaseService +{ + protected override bool Build(FriendPokeEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + switch (input) + { + case GroupPokeEvent group: + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xED3_1 + { + Uin = group.FriendUin, + GroupUin = group.GroupUin, + Ext = 0 + }); + output = packet.Serialize(); + break; + } + case { } friend: + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xED3_1 + { + Uin = friend.FriendUin, + FriendUin = friend.FriendUin, + Ext = 0 + }); + output = packet.Serialize(); + break; + } + default: throw new InvalidDataException(); + } + + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FriendPokeEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = FriendPokeEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PushMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PushMessageService.cs new file mode 100644 index 0000000..018ec2d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/PushMessageService.cs @@ -0,0 +1,386 @@ +using System.Text; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Event.Notify; +using Lagrange.Core.Internal.Packets.Message; +using Lagrange.Core.Internal.Packets.Message.Notify; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Binary; +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(PushMessageEvent))] +[Service("trpc.msg.olpush.OlPushService.MsgPush")] +internal class PushMessageService : BaseService +{ + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out PushMessageEvent output, out List? extraEvents) + { + var message = Serializer.Deserialize(input); + var packetType = (PkgType)message.Message.ContentHead.Type; + + output = null!; + extraEvents = new List(); + switch (packetType) + { + case PkgType.PrivateMessage or PkgType.GroupMessage or PkgType.TempMessage or PkgType.PrivateRecordMessage: + { + var chain = MessagePacker.Parse(message.Message); + output = PushMessageEvent.Create(chain); + break; + } + case PkgType.PrivateFileMessage: + { + var chain = MessagePacker.ParsePrivateFile(message.Message); + output = PushMessageEvent.Create(chain); + break; + } + case PkgType.GroupRequestJoinNotice when message.Message.Body?.MsgContent is { } content: + { + var join = Serializer.Deserialize(content.AsSpan()); + var joinEvent = GroupSysRequestJoinEvent.Result(join.GroupUin, join.TargetUid); + extraEvents.Add(joinEvent); + break; + } + case PkgType.GroupRequestInvitationNotice when message.Message.Body?.MsgContent is { } content: + { + var invitation = Serializer.Deserialize(content.AsSpan()); + if (invitation.Cmd == 87) + { + var info = invitation.Info.Inner; + var invitationEvent = GroupSysRequestInvitationEvent.Result(info.GroupUin, info.TargetUid, info.InvitorUid); + extraEvents.Add(invitationEvent); + } + break; + } + case PkgType.GroupInviteNotice when message.Message.Body?.MsgContent is { } content: + { + var invite = Serializer.Deserialize(content.AsSpan()); + var inviteEvent = GroupSysInviteEvent.Result(invite.GroupUin, invite.InvitorUid); + extraEvents.Add(inviteEvent); + break; + } + case PkgType.GroupAdminChangedNotice when message.Message.Body?.MsgContent is { } content: + { + var admin = Serializer.Deserialize(content.AsSpan()); + bool enabled; string uid; + if (admin.Body.ExtraEnable != null) + { + enabled = true; + uid = admin.Body.ExtraEnable.AdminUid; + } + else if (admin.Body.ExtraDisable != null) + { + enabled = false; + uid = admin.Body.ExtraDisable.AdminUid; + } + else + { + return false; + } + + extraEvents.Add(GroupSysAdminEvent.Result(admin.GroupUin, uid, enabled)); + break; + } + case PkgType.GroupMemberIncreaseNotice when message.Message.Body?.MsgContent is { } content: + { + var increase = Serializer.Deserialize(content.AsSpan()); + var increaseEvent = GroupSysIncreaseEvent.Result(increase.GroupUin, increase.MemberUid, Encoding.UTF8.GetString(increase.Operator.AsSpan()), increase.DecreaseType); + extraEvents.Add(increaseEvent); + break; + } + case PkgType.GroupMemberDecreaseNotice when message.Message.Body?.MsgContent is { } content: + { + var decrease = Serializer.Deserialize(content.AsSpan()); + GroupSysDecreaseEvent decreaseEvent; + if (decrease.DecreaseType == 3) // 3 是bot自身被踢出,Operator字段会是一个protobuf + { + var op = Serializer.Deserialize(decrease.Operator.AsSpan()); + decreaseEvent = GroupSysDecreaseEvent.Result(decrease.GroupUin, decrease.MemberUid, op.Operator.Uid, decrease.DecreaseType); + } + else + { + decreaseEvent = GroupSysDecreaseEvent.Result(decrease.GroupUin, decrease.MemberUid, Encoding.UTF8.GetString(decrease.Operator.AsSpan()), decrease.DecreaseType); + } + extraEvents.Add(decreaseEvent); + break; + } + case PkgType.Event0x210: + { + ProcessEvent0x210(input, message, extraEvents); + break; + } + case PkgType.Event0x2DC: + { + ProcessEvent0x2DC(input, message, extraEvents); + break; + } + default: + { + break; + } + } + return true; + } + + private static void ProcessEvent0x2DC(Span payload, PushMsg msg, List extraEvents) + { + var pkgType = (Event0x2DCSubType)(msg.Message.ContentHead.SubType ?? 0); + switch (pkgType) + { + case Event0x2DCSubType.SubType16 when msg.Message.Body?.MsgContent is { } content: + { + using var packet = new BinaryPacket(content); + _ = packet.ReadUint(); // group uin + _ = packet.ReadByte(); // unknown byte + var proto = packet.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly); // proto length error + var msgBody = Serializer.Deserialize(proto); + switch ((Event0x2DCSubType16Field13)(msgBody.Field13 ?? 0)) + { + case Event0x2DCSubType16Field13.GroupMemberSpecialTitleNotice: + { + break; + } + case Event0x2DCSubType16Field13.GroupNameChangeNotice: + { + // 33CAE9171000450801109B85D0B70618FFFFFFFF0F2097D2AB9E032A0D08011209E686A8E7BEA46F766F680CA802D1DF18AA0118755F6C30323965684E706E4E6A6151725A55687776357551 + var param = Serializer.Deserialize(msgBody.EventParam.AsSpan()); + extraEvents.Add(GroupSysNameChangeEvent.Result(msgBody.GroupUin, param.Name)); + break; + } + case Event0x2DCSubType16Field13.GroupTodoNotice: + { + extraEvents.Add(GroupSysTodoEvent.Result(msgBody.GroupUin, msgBody.OperatorUid)); + break; + } + case Event0x2DCSubType16Field13.GroupReactionNotice: + { + uint group = msgBody.GroupUin; + string uid = msgBody.Reaction.Data.Data.Data.OperatorUid; + uint type = msgBody.Reaction.Data.Data.Data.Type; + uint sequence = msgBody.Reaction.Data.Data.Target.Sequence; + string code = msgBody.Reaction.Data.Data.Data.Code; + uint count = msgBody.Reaction.Data.Data.Data.Count; + var groupRecallEvent = GroupSysReactionEvent.Result(group, sequence, uid, type == 1, code, count); + extraEvents.Add(groupRecallEvent); + break; + } + } + break; + } + case Event0x2DCSubType.GroupRecallNotice when msg.Message.Body?.MsgContent is { } content: + { + using var packet = new BinaryPacket(content); + _ = packet.ReadUint(); // group uin + _ = packet.ReadByte(); // unknown byte + var proto = packet.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly); + var recall = Serializer.Deserialize(proto); + var meta = recall.Recall.RecallMessages[0]; + var groupRecallEvent = GroupSysRecallEvent.Result( + recall.GroupUin, + meta.AuthorUid, + recall.Recall.OperatorUid, + meta.Sequence, + meta.Time, + meta.Random, + recall?.Recall.TipInfo?.Tip ?? "" + ); + extraEvents.Add(groupRecallEvent); + break; + } + case Event0x2DCSubType.GroupMuteNotice when msg.Message.Body?.MsgContent is { } content: + { + var mute = Serializer.Deserialize(content.AsSpan()); + if (mute.Data.State.TargetUid == null) + { + var groupMuteEvent = GroupSysMuteEvent.Result(mute.GroupUin, mute.OperatorUid, mute.Data.State.Duration != 0); + extraEvents.Add(groupMuteEvent); + } + else + { + var memberMuteEvent = GroupSysMemberMuteEvent.Result(mute.GroupUin, mute.OperatorUid, mute.Data.State.TargetUid, mute.Data.State.Duration); + extraEvents.Add(memberMuteEvent); + } + break; + } + case Event0x2DCSubType.GroupEssenceNotice when msg.Message.Body?.MsgContent is { } content: + { + using var packet = new BinaryPacket(content); + _ = packet.ReadUint(); // group uin + _ = packet.ReadByte(); // unknown byte + var proto = packet.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly); + var essence = Serializer.Deserialize(proto); + var essenceMsg = essence.EssenceMessage; + var groupEssenceEvent = GroupSysEssenceEvent.Result(essenceMsg.GroupUin, essenceMsg.MsgSequence, + essenceMsg.Random, essenceMsg.SetFlag, essenceMsg.MemberUin, essenceMsg.OperatorUin); + extraEvents.Add(groupEssenceEvent); + break; + } + case Event0x2DCSubType.GroupGreyTipNotice when msg.Message.Body?.MsgContent is { } content: + { + using var packet = new BinaryPacket(content); + uint groupUin = packet.ReadUint(); // group uin + _ = packet.ReadByte(); // unknown byte + var proto = packet.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly); + var greyTip = Serializer.Deserialize(proto); + + var templates = greyTip.GeneralGrayTip.MsgTemplParam.ToDictionary(x => x.Name, x => x.Value); + + if (!templates.TryGetValue("action_str", out var actionStr) && !templates.TryGetValue("alt_str1", out actionStr)) + { + actionStr = string.Empty; + } + + if (greyTip.GeneralGrayTip.BusiType == 12) // poke + { + var groupPokeEvent = GroupSysPokeEvent.Result(groupUin, uint.Parse(templates["uin_str1"]), uint.Parse(templates["uin_str2"]), actionStr, templates["suffix_str"], templates["action_img_url"]); + extraEvents.Add(groupPokeEvent); + } + break; + } + default: + { + break; + } + } + } + + private static void ProcessEvent0x210(Span payload, PushMsg msg, List extraEvents) + { + var pkgType = (Event0x210SubType)(msg.Message.ContentHead.SubType ?? 0); + switch (pkgType) + { + case Event0x210SubType.FriendRequestNotice when msg.Message.Body?.MsgContent is { } content: + { + if (Serializer.Deserialize(content.AsSpan()).Info is { } info) + { + var friendEvent = FriendSysRequestEvent.Result(msg.Message.ResponseHead.FromUin, info.SourceUid, info.Message, info.Source); + extraEvents.Add(friendEvent); + } + break; + } + case Event0x210SubType.GroupMemberEnterNotice when msg.Message.Body?.MsgContent is { } content: + { + var info = Serializer.Deserialize(content.AsSpan()); + if (info is { Body.Info.Detail: { Style: { } style } detail }) + { + var @event = GroupSysMemberEnterEvent.Result(detail.GroupId, detail.GroupMemberUin, style.StyleId); + extraEvents.Add(@event); + } + break; + } + case Event0x210SubType.FriendDeleteOrPinChangedNotice when msg.Message.Body?.MsgContent is { } content: // Stupid TX + { + var info = Serializer.Deserialize(content.AsSpan()); + // if (info.Body.Data.Type == 5 && info.Body.Data.FriendDelete != null) // Friend Delete + // 0A8D010A4008AFB39FF80A1218755F54305768425A6368695A684555496253786F6F63474128AFB39FF80A3218755F54305768425A6368695A684555496253786F6F634741122108900410271827209092D9C10228F2C00330809096AF06609092D9C182808080021A260A0012220A2008001005721A0A18755F597831586B5A4E4E656E4E3141356A53423361576667 + if (info.Body.Type == 7 && info.Body.PinChanged is { } data) + { + var @event = SysPinChangedEvent.Result( + data.Body.Uid, + data.Body.GroupUin, + data.Body.Info.Timestamp.Length != 0 + ); + extraEvents.Add(@event); + } + break; + } + case Event0x210SubType.FriendRecallNotice when msg.Message.Body?.MsgContent is { } content: + { + var recall = Serializer.Deserialize(content.AsSpan()); + var recallEvent = FriendSysRecallEvent.Result( + recall.Info.FromUid, + recall.Info.ClientSequence, + recall.Info.Time, + recall.Info.Random, + recall.Info.TipInfo.Tip ?? "" + ); + extraEvents.Add(recallEvent); + break; + } + case Event0x210SubType.ServicePinChanged: + { + // 0A93010A4008E2FBEDF9051218755F667132684B3132624267306F7735637A57685A66726728E2FBEDF9053218755F667132684B3132624267306F7735637A57685A667267122308900410C70118C70120B7E1C1B50528B8CD0330A790E2B90660B7E1C1B585808080021A2A0A0012260A24080010A01F82FA011B08934E10E2FBEDF90518894E2003320B0831CADFEF010467388827180122330A0D33302E3138362E38362E31363410FE9D011A1E10900418B8CD0320B7E1C1B5858080800230C701380140E2FBEDF9054801 + break; + } + case Event0x210SubType.GroupKickNotice when msg.Message.Body?.MsgContent is { } content: + { + // 0A710A4008AFB39FF80A1218755F54305768425A6368695A684555496253786F6F63474128AFB39FF80A3218755F54305768425A6368695A684555496253786F6F634741122108900410D40118D4012090845428A0850230ECB982AF06609084D48080808080021A0A0A00120608BDCCF4E802180122340A0E33302E3137312E3135392E32333510FE9D011A1E10900418A08502209084D480808080800230D401380140AFB39FF80A4801 + break; + } + case Event0x210SubType.FriendPokeNotice when msg.Message.Body?.MsgContent is { } content: + { + var greyTip = Serializer.Deserialize(content.AsSpan()); + var templates = greyTip.MsgTemplParam.ToDictionary(x => x.Name, x => x.Value); + + if (!templates.TryGetValue("action_str", out var actionStr) && !templates.TryGetValue("alt_str1", out actionStr)) + { + actionStr = string.Empty; + } + + if (greyTip.BusiType == 12) // poke + { + var friendPokeEvent = FriendSysPokeEvent.Result(uint.Parse(templates["uin_str1"]), uint.Parse(templates["uin_str2"]), actionStr, templates["suffix_str"], templates["action_img_url"]); + extraEvents.Add(friendPokeEvent); + } + break; + } + default: + { + break; + } + } + } + + private enum PkgType + { + PrivateMessage = 166, + GroupMessage = 82, + TempMessage = 141, + + Event0x210 = 528, // friend related event + Event0x2DC = 732, // group related event + + PrivateRecordMessage = 208, + PrivateFileMessage = 529, + + GroupRequestInvitationNotice = 525, // from group member invitation + GroupRequestJoinNotice = 84, // directly entered + GroupInviteNotice = 87, // the bot self is being invited + GroupAdminChangedNotice = 44, // admin change, both on and off + GroupMemberIncreaseNotice = 33, + GroupMemberDecreaseNotice = 34, + } + + private enum Event0x2DCSubType + { + GroupMuteNotice = 12, + SubType16 = 16, + GroupRecallNotice = 17, + GroupEssenceNotice = 21, + GroupGreyTipNotice = 20, + } + + private enum Event0x2DCSubType16Field13 + { + GroupMemberSpecialTitleNotice = 6, + GroupNameChangeNotice = 12, + GroupTodoNotice = 23, + GroupReactionNotice = 35, + } + + private enum Event0x210SubType + { + FriendRequestNotice = 35, + GroupMemberEnterNotice = 38, + FriendDeleteOrPinChangedNotice = 39, + FriendRecallNotice = 138, + ServicePinChanged = 199, // e.g: My computer | QQ Wallet | ... + FriendPokeNotice = 290, + GroupKickNotice = 212, + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallFriendMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallFriendMessageService.cs new file mode 100644 index 0000000..ba0566b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallFriendMessageService.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecallFriendMessageEvent))] +[Service("trpc.msg.msg_svc.MsgService.SsoC2CRecallMsg")] +internal class RecallFriendMessageService : BaseService +{ + protected override bool Build(RecallFriendMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new C2CRecallMsg + { + Type = 1, + TargetUid = input.TargetUid, + Info = new C2CRecallMsgInfo + { + ClientSequence = input.ClientSeq, + Random = input.Random, + MessageId = 0x01000000UL << 32 | input.Random, + Timestamp = input.Timestamp, + Field5 = 0, + MessageSequence = input.MessageSeq + }, + Settings = new C2CRecallMsgSettings + { + Field1 = false, + Field2 = false + }, + Field6 = false + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecallFriendMessageEvent output, out List? extraEvents) + { + output = RecallFriendMessageEvent.Result(0); // 腾讯自己都不知道有没有这条消息 都不会报错的 应该是交给客户端处理了 + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallGroupMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallGroupMessageService.cs new file mode 100644 index 0000000..2239583 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecallGroupMessageService.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecallGroupMessageEvent))] +[Service("trpc.msg.msg_svc.MsgService.SsoGroupRecallMsg")] +internal class RecallGroupMessageService : BaseService +{ + protected override bool Build(RecallGroupMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new GroupRecallMsg + { + Type = 1, + GroupUin = input.GroupUin, + Field3 = new GroupRecallMsgField3 + { + Sequence = input.Sequence, + Field3 = 0 + }, + Field4 = new GroupRecallMsgField4 { Field1 = 0 } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecallGroupMessageEvent output, out List? extraEvents) + { + output = RecallGroupMessageEvent.Result(0); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordDownloadService.cs new file mode 100644 index 0000000..13438af --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordDownloadService.cs @@ -0,0 +1,73 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecordDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x126d_200")] +internal class RecordDownloadService : BaseService +{ + protected override bool Build(RecordDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 200 + }, + Scene = new SceneInfo + { + RequestType = 1, + BusinessType = 3, + SceneType = 1, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = input.SelfUid + } + }, + Client = new ClientMeta { AgentType = 2 } + }, + Download = new DownloadReq + { + Node = input.Node ?? new IndexNode + { + FileUuid = input.FileUuid + }, + Download = new DownloadExt + { + Video = new VideoDownloadExt + { + BusiType = 0, + SceneType = 0 + } + } + } + }, 0x126d, 200, false, true); + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecordDownloadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var body = payload.Body.Download; + string url = $"https://{body.Info.Domain}{body.Info.UrlPath}{body.RKeyParam}"; + + output = RecordDownloadEvent.Result((int)payload.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupDownloadService.cs new file mode 100644 index 0000000..1a26d6a --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupDownloadService.cs @@ -0,0 +1,69 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecordGroupDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x126e_200")] +internal class RecordGroupDownloadService : BaseService +{ + protected override bool Build(RecordGroupDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 4, + Command = 200 + }, + Scene = new SceneInfo + { + RequestType = 1, + BusinessType = 3, + SceneType = 2, + Group = new GroupInfo { GroupUin = input.GroupUin } + }, + Client = new ClientMeta { AgentType = 2 } + }, + Download = new DownloadReq + { + Node = input.Node ?? new IndexNode + { + FileUuid = input.FileUuid + }, + Download = new DownloadExt + { + Video = new VideoDownloadExt + { + BusiType = 0, + SceneType = 0 + } + } + } + }, 0x126e, 200, false, true); + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecordGroupDownloadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var body = payload.Body.Download; + string url = $"https://{body.Info.Domain}{body.Info.UrlPath}{body.RKeyParam}"; + + output = RecordGroupDownloadEvent.Result((int)payload.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupUploadService.cs new file mode 100644 index 0000000..3c40d71 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordGroupUploadService.cs @@ -0,0 +1,108 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Component; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; +using GroupInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.GroupInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecordGroupUploadEvent))] +[Service("OidbSvcTrpcTcp.0x126e_100")] +internal class RecordGroupUploadService : BaseService +{ + protected override bool Build(RecordGroupUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.AudioStream is null) throw new Exception(); + + string md5 = input.Entity.AudioStream.Value.Md5(true); + string sha1 = input.Entity.AudioStream.Value.Sha1(true); + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1u, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 3, + SceneType = 2u, + Group = new GroupInfo { GroupUin = input.GroupUin } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.AudioStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = md5 + ".amr", + Type = new FileType + { + Type = 3, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 1 + }, + Width = 0, + Height = 0, + Time = (uint)input.Entity.AudioLength, + Original = 0 + }, + SubFileType = 0 + } + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 2u, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo { TextSummary = "" }, + Video = new VideoExtBizInfo { BytesPbReserve = Array.Empty() }, + Ptt = new PttExtBizInfo + { + BytesReserve = Array.Empty(), + BytesPbReserve = new byte[] { 0x08, 0x00, 0x38, 0x00 }, + BytesGeneralFlags = new byte[] { 0x9a, 0x01, 0x07, 0xaa, 0x03, 0x04, 0x08, 0x08, 0x12, 0x00 } + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x126e, 100, false, true); + + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecordGroupUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(upload.CompatQMsg.AsSpan()); + + output = RecordGroupUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordUploadService.cs new file mode 100644 index 0000000..233a3b0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RecordUploadService.cs @@ -0,0 +1,110 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Component; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RecordUploadEvent))] +[Service("OidbSvcTrpcTcp.0x126d_100")] +internal class RecordUploadService : BaseService +{ + protected override bool Build(RecordUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.AudioStream is null) throw new Exception(); + + string md5 = input.Entity.AudioStream.Value.Md5(true); + string sha1 = input.Entity.AudioStream.Value.Sha1(true); + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 4u, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 3, + SceneType = 1u, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = input.TargetUid + } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.AudioStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = md5 + ".amr", + Type = new FileType + { + Type = 3, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 1 + }, + Width = 0, + Height = 0, + Time = (uint)input.Entity.AudioLength, + Original = 0 + }, + SubFileType = 0 + } + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 1u, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo { TextSummary = "" }, + Video = new VideoExtBizInfo { BytesPbReserve = Array.Empty() }, + Ptt = new PttExtBizInfo + { + BytesReserve = new byte[] { 0x08, 0x00, 0x38, 0x00 }, + BytesPbReserve = Array.Empty(), + BytesGeneralFlags = new byte[] { 0x9a, 0x01, 0x0b, 0xaa, 0x03, 0x08, 0x08, 0x04, 0x12, 0x04, 0x00, 0x00, 0x00, 0x00 } + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x126d, 100, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RecordUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(upload.CompatQMsg.AsSpan()); + + output = RecordUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RemoveEssenceMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RemoveEssenceMessageService.cs new file mode 100644 index 0000000..1f01754 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/RemoveEssenceMessageService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(RemoveEssenceMessageEvent))] +[Service("OidbSvcTrpcTcp.0xeac_2")] +internal class RemoveEssenceMessageService : BaseService +{ + protected override bool Build(RemoveEssenceMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xEAC + { + GroupUin = input.GroupUin, + Sequence = input.Sequence, + Random = input.Random + }, 0xeac, 2); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out RemoveEssenceMessageEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = RemoveEssenceMessageEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SendMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SendMessageService.cs new file mode 100644 index 0000000..d46adca --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SendMessageService.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Action; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(SendMessageEvent))] +[Service("MessageSvc.PbSendMsg")] +internal class SendMessageService : BaseService +{ + protected override bool Build(SendMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = MessagePacker.Build(input.Chain, keystore.Uid ?? throw new Exception("No UID found in keystore")); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SendMessageEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize(input); + var result = new MessageResult + { + Result = (uint)response.Result, + Sequence = response.GroupSequence ?? response.PrivateSequence, + Timestamp = response.Timestamp1, + }; + + output = SendMessageEvent.Result(response.Result, result); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SetEssenceMessageService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SetEssenceMessageService.cs new file mode 100644 index 0000000..8941359 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/SetEssenceMessageService.cs @@ -0,0 +1,39 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(SetEssenceMessageEvent))] +[Service("OidbSvcTrpcTcp.0xeac_1")] +internal class SetEssenceMessageService : BaseService +{ + protected override bool Build(SetEssenceMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xEAC + { + GroupUin = input.GroupUin, + Sequence = input.Sequence, + Random = input.Random + }, 0xeac, 1); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetEssenceMessageEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetEssenceMessageEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoDownloadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoDownloadService.cs new file mode 100644 index 0000000..3ad1a5d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoDownloadService.cs @@ -0,0 +1,99 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(VideoDownloadEvent))] +[Service("OidbSvcTrpcTcp.0x11e9_200")] +internal class VideoDownloadService : BaseService +{ + protected override bool Build(VideoDownloadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = input.IsGroup ? 3u : 34u, + Command = 200 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 2, + SceneType = 1, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = input.SelfUid + } + }, + Client = new ClientMeta + { + AgentType = 2 + } + }, + Download = new DownloadReq + { + Node = new IndexNode + { + Info = new FileInfo + { + FileSize = 0, + FileHash = input.FileMd5, + FileSha1 = input.FileSha1 ?? "", + FileName = input.FileName, + Type = new FileType + { + Type = 2, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = 0, + Height = 0, + Time = 0, + Original = 0 + }, + FileUuid = input.Uuid, + StoreId = 0, + UploadTime = 0, + Ttl = 0, + SubType = 0 + }, + Download = new DownloadExt + { + Video = new VideoDownloadExt + { + BusiType = 0, + SceneType = 0 + } + } + } + }, 0x11e9, 200, false, true); + output = packet.Serialize(); + extraPackets = null; + + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out VideoDownloadEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var body = payload.Body.Download; + string url = $"https://{body.Info.Domain}{body.Info.UrlPath}{body.RKeyParam}"; + + output = VideoDownloadEvent.Result((int)payload.ErrorCode, url); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoGroupUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoGroupUploadService.cs new file mode 100644 index 0000000..a377e69 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoGroupUploadService.cs @@ -0,0 +1,132 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; +using GroupInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.GroupInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(VideoGroupUploadEvent))] +[Service("OidbSvcTrpcTcp.0x11ea_100")] +internal class VideoGroupUploadService : BaseService +{ + protected override bool Build(VideoGroupUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.VideoStream is null || input.Entity.ThumbnailStream is null) throw new Exception(); + + string md5 = input.Entity.VideoStream.Value.Md5(true); + string sha1 = input.Entity.VideoStream.Value.Sha1(true); + + string thumbMd5 = input.Entity.ThumbnailStream.Value.Md5(true); + string thumbSha1 = input.Entity.ThumbnailStream.Value.Sha1(true); + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 3, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 2, + SceneType = 2, + Group = new GroupInfo { GroupUin = input.GroupUin } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.VideoStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = "video.mp4", + Type = new FileType + { + Type = 2, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = 0, + Height = 0, + Time = (uint)input.Entity.VideoLength, + Original = 0 + }, + SubFileType = 0 + }, + new() + { + FileInfo = new FileInfo // dummy images + { + FileSize = (uint)input.Entity.ThumbnailStream.Value.Length, + FileHash = thumbMd5, + FileSha1 = thumbSha1, + FileName = "video.jpg", + Type = new FileType + { + Type = 1, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = 1920, + Height = 1080, + Time = 0, + Original = 0 + }, + SubFileType = 100 + }, + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 2, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo { BizType = 0, TextSummary = "" }, + Video = new VideoExtBizInfo { BytesPbReserve = "800100".UnHex() }, + Ptt = new PttExtBizInfo + { + BytesReserve = Array.Empty(), + BytesPbReserve = Array.Empty(), + BytesGeneralFlags = Array.Empty() + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x11ea, 100, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out VideoGroupUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(packet.Body.Upload.CompatQMsg.AsSpan()); + + output = VideoGroupUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoUploadService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoUploadService.cs new file mode 100644 index 0000000..b827f48 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/Message/VideoUploadService.cs @@ -0,0 +1,135 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using FileInfo = Lagrange.Core.Internal.Packets.Service.Oidb.Common.FileInfo; + +namespace Lagrange.Core.Internal.Service.Message; + +[EventSubscribe(typeof(VideoUploadEvent))] +[Service("OidbSvcTrpcTcp.0x11e9_100")] +internal class VideoUploadService : BaseService +{ + protected override bool Build(VideoUploadEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + if (input.Entity.VideoStream is null || input.Entity.ThumbnailStream is null) throw new Exception(); + + string md5 = input.Entity.VideoStream.Value.Md5(true); + string sha1 = input.Entity.VideoStream.Value.Sha1(true); + + string thumbMd5 = input.Entity.ThumbnailStream.Value.Md5(true); + string thumbSha1 = input.Entity.ThumbnailStream.Value.Sha1(true); + + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 3, + Command = 100 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 2, + SceneType = 1, + C2C = new C2CUserInfo + { + AccountType = 2, + TargetUid = input.TargetUid + } + }, + Client = new ClientMeta { AgentType = 2 }, + }, + Upload = new UploadReq + { + UploadInfo = new List + { + new() + { + FileInfo = new FileInfo + { + FileSize = (uint)input.Entity.VideoStream.Value.Length, + FileHash = md5, + FileSha1 = sha1, + FileName = "video.mp4", + Type = new FileType + { + Type = 2, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = 0, + Height = 0, + Time = (uint)input.Entity.VideoLength, + Original = 0 + }, + SubFileType = 0 + }, + new() + { + FileInfo = new FileInfo // dummy images + { + FileSize = (uint)input.Entity.ThumbnailStream.Value.Length, + FileHash = thumbMd5, + FileSha1 = thumbSha1, + FileName = "video.jpg", + Type = new FileType + { + Type = 1, + PicFormat = 0, + VideoFormat = 0, + VoiceFormat = 0 + }, + Width = 1920, + Height = 1080, + Time = 0, + Original = 0 + }, + SubFileType = 100 + }, + }, + TryFastUploadCompleted = true, + SrvSendMsg = false, + ClientRandomId = (ulong)Random.Shared.Next(), + CompatQMsgSceneType = 2, + ExtBizInfo = new ExtBizInfo + { + Pic = new PicExtBizInfo { BizType = 0, TextSummary = "" }, + Video = new VideoExtBizInfo { BytesPbReserve = "800100".UnHex() }, + Ptt = new PttExtBizInfo + { + BytesReserve = Array.Empty(), + BytesPbReserve = Array.Empty(), + BytesGeneralFlags = Array.Empty() + } + }, + ClientSeq = 0, + NoNeedCompatMsg = false + } + }, 0x11e9, 100, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out VideoUploadEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var upload = packet.Body.Upload; + var compat = Serializer.Deserialize(packet.Body.Upload.CompatQMsg.AsSpan()); + + output = VideoUploadEvent.Result((int)packet.ErrorCode, upload.MsgInfo, upload.UKey, upload.IPv4s, upload.SubFileInfos, compat); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/ServiceAttribute.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/ServiceAttribute.cs new file mode 100644 index 0000000..47f0f87 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/ServiceAttribute.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Internal.Service; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal class ServiceAttribute : Attribute +{ + public string Command { get; } + + public byte PacketType { get; set; } + + public ServiceAttribute(string command, byte packetType = 12) + { + Command = command; + PacketType = packetType; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/AliveService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/AliveService.cs new file mode 100644 index 0000000..6a230e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/AliveService.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(AliveEvent))] +[Service("Heartbeat.Alive", 13)] +internal class AliveService : BaseService +{ + protected override bool Build(AliveEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + output = new BinaryPacket().WriteUint(4).ToArray(); + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/CorrectTimeService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/CorrectTimeService.cs new file mode 100644 index 0000000..a5a207f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/CorrectTimeService.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(CorrectTimeEvent))] +[Service("Client.CorrectTime", 13)] +internal class CorrectTimeService : BaseService +{ + protected override bool Build(CorrectTimeEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + output = new BinaryPacket().WriteUint(4).ToArray(); + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchClientKeyService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchClientKeyService.cs new file mode 100644 index 0000000..5cdc0d6 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchClientKeyService.cs @@ -0,0 +1,35 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchClientKeyEvent))] +[Service("OidbSvcTrpcTcp.0x102a_1")] +internal class FetchClientKeyService : BaseService +{ + protected override bool Build(FetchClientKeyEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x102A_1()); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchClientKeyEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = FetchClientKeyEvent.Result(0, packet.Body.ClientKey); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchCookieService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchCookieService.cs new file mode 100644 index 0000000..841a4f3 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchCookieService.cs @@ -0,0 +1,40 @@ +using System.Text; +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchCookieEvent))] +[Service("OidbSvcTrpcTcp.0x102a_0")] +internal class FetchCookieService : BaseService +{ + protected override bool Build(FetchCookieEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x102A_0 + { + Domain = input.Domains + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchCookieEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var cookies = packet.Body.Urls.Select(x => Encoding.UTF8.GetString(x.Value)).ToList(); + + output = FetchCookieEvent.Result((int)packet.ErrorCode, cookies); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFilteredGroupRequestsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFilteredGroupRequestsService.cs new file mode 100644 index 0000000..216ff95 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFilteredGroupRequestsService.cs @@ -0,0 +1,53 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchGroupRequestsEvent))] +[Service("OidbSvcTrpcTcp.0x10c0_2")] +internal class FetchFilteredGroupRequestsService : BaseService +{ + protected override bool Build(FetchGroupRequestsEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x10C0_2 + { + Count = 20, + Field2 = 0 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out FetchGroupRequestsEvent output, + out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var events = payload.Body.Requests?.Select(x => new FetchGroupRequestsEvent.RawEvent( + x.Group.GroupUin, + x.Invitor?.Uid, + x.Invitor?.Name, + x.Target.Uid, + x.Target.Name, + x.Operator?.Uid, + x.Operator?.Name, + x.Sequence, + x.State, + x.EventType, + x.Comment, + true + )).ToList() ?? new List(); + + output = FetchGroupRequestsEvent.Result((int)payload.ErrorCode, events); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsRequestsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsRequestsService.cs new file mode 100644 index 0000000..09ae4a0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsRequestsService.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchFriendsRequestsEvent))] +[Service("OidbSvcTrpcTcp.0x5cf_11")] +internal class FetchFriendsRequestsService : BaseService +{ + protected override bool Build(FetchFriendsRequestsEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x5CF_11 + { + Field1 = 1, + Field3 = 6, + SelfUid = keystore.Uid ?? "", + Field5 = 0, + Field6 = 80, + Field8 = 2, + Field9 = 0, + Field12 = 1, + Field22 = 1 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchFriendsRequestsEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var requests = payload.Body.Info.Requests.Select(r => new BotFriendRequest(r.TargetUid, r.SourceUid, r.State, r.Comment, r.Source, r.Timestamp)).ToList(); + + extraEvents = null; + output = FetchFriendsRequestsEvent.Result((int)payload.ErrorCode, requests); + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsService.cs new file mode 100644 index 0000000..46c1980 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFriendsService.cs @@ -0,0 +1,72 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Generics; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchFriendsEvent))] +[Service("OidbSvcTrpcTcp.0xfd4_1")] +internal class FetchFriendsService : BaseService +{ + private const int MaxFriendCount = 300; + + protected override bool Build(FetchFriendsEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xFD4_1 + { + FriendCount = MaxFriendCount, // max value + Body = new List + { + new() { Type = 1, Number = new OidbNumber { Numbers = { 103, 102, 20002, 27394 } } }, + new() { Type = 4, Number = new OidbNumber { Numbers = { 100, 101, 102 } } } + } + }); + + if (input.NextUin != null) packet.Body.NextUin = new OidbSvcTrpcTcp0xFD4_1Uin { Uin = input.NextUin.Value }; + + /* + * OidbNumber里面的东西代表你想要拿到的Property,这些Property将会在返回的数据里面的Preserve的Field, + * 102:个性签名 + * 103:备注 + * 20002:昵称 + * 27394:QID + */ + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchFriendsEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + var friends = new List(); + foreach (var raw in packet.Body.Friends) + { + var additional = raw.Additional.First(x => x.Type == 1); + var properties = Property(additional.Layer1.Properties); + friends.Add(new BotFriend(raw.Uin, raw.Uid, properties.ContainsKey(20002) ? properties[20002] : raw.Uin.ToString(), properties[103], properties[102], properties.ContainsKey(27394) ? properties[27394] : "", new(raw.CustomGroup, ""))); + } + + output = FetchFriendsEvent.Result(0, friends, packet.Body.Groups.ToDictionary(k => k.Code, v => v.Value), packet.Body.Next?.Uin); // 全家4完了才能想出来这种分页的逻辑 + extraEvents = null; + return true; + } + + private static Dictionary Property(List properties) + { + var dict = new Dictionary(properties.Capacity); + foreach (var property in properties) dict[property.Code] = property.Value; + return dict; + } +} diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFullSysFacesService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFullSysFacesService.cs new file mode 100644 index 0000000..681c12c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchFullSysFacesService.cs @@ -0,0 +1,68 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +// TODO: onebot11 API (? +[EventSubscribe(typeof(FetchFullSysFacesEvent))] +[Service("OidbSvcTrpcTcp.0x9154_1")] +internal class FetchFullSysFacesService : BaseService +{ + protected override bool Build(FetchFullSysFacesEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x9154_1 + { + Field1 = 0, + Field2 = 7, + Field3 = "0", + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchFullSysFacesEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var response = packet.Body; + + var emojiPackList = new[] { response.CommonFace, response.SpecialBigFace } + .SelectMany(content => content.EmojiList) + .Select( + emojiList => new SysFacePackEntry( + emojiList.EmojiPackName, emojiList.EmojiDetail.Select( + emoji => new SysFaceEntry( + emoji.QSid, emoji.QDes, emoji.EMCode, emoji.QCid, emoji.AniStickerType, + emoji.AniStickerPackId, emoji.AniStickerId, emoji.Url?.BaseUrl, + emoji.EmojiNameAlias, emoji.AniStickerWidth, emoji.AniStickerHeight + ) + ).ToArray() + ) + ).ToList(); + + var magicFaceList = response.SpecialMagicFace.Field1.EmojiList.Select( + emoji => new SysFaceEntry( + emoji.QSid, emoji.QDes, emoji.EMCode, emoji.QCid, emoji.AniStickerType, + emoji.AniStickerPackId, emoji.AniStickerId, emoji.Url?.BaseUrl, + emoji.EmojiNameAlias, emoji.AniStickerWidth, emoji.AniStickerHeight + ) + ).ToList(); + + var magicFacePackEntry = new SysFacePackEntry("MagicFace", magicFaceList.ToArray()); + emojiPackList.Add(magicFacePackEntry); + + output = FetchFullSysFacesEvent.Result((int)packet.ErrorCode, emojiPackList); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupRequestsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupRequestsService.cs new file mode 100644 index 0000000..c78d15d --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupRequestsService.cs @@ -0,0 +1,53 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchGroupRequestsEvent))] +[Service("OidbSvcTrpcTcp.0x10c0_1")] +internal class FetchGroupRequestsService : BaseService +{ + protected override bool Build(FetchGroupRequestsEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x10C0_1 + { + Count = 20, + Field2 = 0 + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out FetchGroupRequestsEvent output, + out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + var events = payload.Body.Requests?.Select(x => new FetchGroupRequestsEvent.RawEvent( + x.Group.GroupUin, + x.Invitor?.Uid, + x.Invitor?.Name, + x.Target.Uid, + x.Target.Name, + x.Operator?.Uid, + x.Operator?.Name, + x.Sequence, + x.State, + x.EventType, + x.Comment, + false + )).ToList() ?? new List(); + + output = FetchGroupRequestsEvent.Result((int)payload.ErrorCode, events); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupsService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupsService.cs new file mode 100644 index 0000000..3a7371b --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchGroupsService.cs @@ -0,0 +1,50 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchGroupsEvent))] +[Service("OidbSvcTrpcTcp.0xfe5_2")] +internal class FetchGroupsService : BaseService +{ + protected override bool Build(FetchGroupsEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xFE5_2 + { + Config = new OidbSvcTrpcTcp0xFE5_2Config + { + Config1 = new OidbSvcTrpcTcp0xFE5_2Config1(), + Config2 = new OidbSvcTrpcTcp0xFE5_2Config2(), + Config3 = new OidbSvcTrpcTcp0xFE5_2Config3() + } + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchGroupsEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + var groups = payload.Body.Groups.Select(raw => + { + var i = raw.Info; + return new BotGroup(raw.GroupUin, i.GroupName, i.MemberCount, i.MemberMax, i.CreatedTime, i.Description, i.Question, i.Announcement); + }).ToList(); + + output = FetchGroupsEvent.Result(groups); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchMembersService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchMembersService.cs new file mode 100644 index 0000000..5c499c0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchMembersService.cs @@ -0,0 +1,65 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchMembersEvent))] +[Service("OidbSvcTrpcTcp.0xfe7_3")] +internal class FetchMembersService : BaseService +{ + protected override bool Build(FetchMembersEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xFE7_3 + { + GroupUin = input.GroupUin, + Field2 = 5, + Field3 = 2, + Body = new OidbSvcTrpcScp0xFE7_3Body + { + MemberName = true, + MemberCard = true, + SpecialTitle = true, + Level = true, + JoinTimestamp = true, + LastMsgTimestamp = true, + ShutUpTimestamp = true, + Permission = true, + }, + Token = input.Token + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchMembersEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize>(input); + + var members = response.Body.Members.Select(member => + new BotGroupMember(member.Uin.Uin, + member.Uin.Uid, + (GroupMemberPermission)member.Permission, + member.Level?.Level ?? 0, + member.MemberCard.MemberCard, + member.MemberName, + member.SpecialTitle ?? "", + DateTimeOffset.FromUnixTimeSeconds(member.JoinTimestamp).LocalDateTime, + DateTimeOffset.FromUnixTimeSeconds(member.LastMsgTimestamp).LocalDateTime, + DateTimeOffset.FromUnixTimeSeconds(member.ShutUpTimestamp ?? 0).LocalDateTime)).ToList(); + + output = FetchMembersEvent.Result(members, response.Body.Token); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchRKeyService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchRKeyService.cs new file mode 100644 index 0000000..3dd0934 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchRKeyService.cs @@ -0,0 +1,55 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchRKeyEvent))] +[Service("OidbSvcTrpcTcp.0x9067_202")] +internal class FetchRKeyService : BaseService +{ + protected override bool Build(FetchRKeyEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new NTV2RichMediaReq + { + ReqHead = new MultiMediaReqHead + { + Common = new CommonHead + { + RequestId = 1, + Command = 202 + }, + Scene = new SceneInfo + { + RequestType = 2, + BusinessType = 1, + SceneType = 0 + }, + Client = new ClientMeta { AgentType = 2 } + }, + DownloadRKey = new DownloadRKeyReq + { + Types = new List { 10, 20, 2 } + } + }, 0x9067, 202, false, true); + + extraPackets = null; + output = packet.Serialize(); + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchRKeyEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = FetchRKeyEvent.Result((int)payload.ErrorCode, payload.Body.DownloadRKey.RKeys); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchUserInfoService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchUserInfoService.cs new file mode 100644 index 0000000..e9cb5f0 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/FetchUserInfoService.cs @@ -0,0 +1,187 @@ +using System.Text; +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using static Lagrange.Core.Common.Entity.BotUserInfo; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(FetchUserInfoEvent))] +[Service("OidbSvcTrpcTcp.0xfe1_2")] +internal class FetchUserInfoService : BaseService +{ + private static readonly List _keys = new() + { + new() { Key = 20002 }, //昵称 + new() { Key = 27394 }, //QID + new() { Key = 20009 }, //性别 + new() { Key = 20031 }, //生日 + new() { Key = 101 }, //头像 + new() { Key = 103 }, + new() { Key = 102 }, //简介/签名 + new() { Key = 20022 }, + new() { Key = 20023 }, + new() { Key = 20024 }, + new() { Key = 24002 }, + new() { Key = 27037 }, + new() { Key = 27049 }, + new() { Key = 20011 }, + new() { Key = 20016 }, + new() { Key = 20021 }, //学校 + new() { Key = 20003 }, //国家 + new() { Key = 20004 }, + new() { Key = 20005 }, + new() { Key = 20006 }, + new() { Key = 20020 }, //城市 + new() { Key = 20026 }, //注册时间 + new() { Key = 24007 }, + new() { Key = 104 }, + new() { Key = 105 }, //等级 + new() { Key = 42432 }, + new() { Key = 42362 }, + new() { Key = 41756 }, + new() { Key = 41757 }, + new() { Key = 42257 }, + new() { Key = 27372 }, //状态 + new() { Key = 42315 }, + new() { Key = 107 }, //业务列表 + new() { Key = 45160 }, + new() { Key = 45161 }, + new() { Key = 27406 }, //自定义状态文本 + new() { Key = 62026 }, + new() { Key = 20037 } //年龄 + }; + + protected override bool Build(FetchUserInfoEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + + output = input.Uid == null + ? new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xFE1_2Uin + { + Uin = input.Uin, + Field2 = 0, + Keys = _keys, + }, 0xfe1, 2, false, true).Serialize() + : new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xFE1_2Uid + { + Uid = input.Uid, + Field2 = 0, + Keys = _keys + }, 0xfe1, 2).Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchUserInfoEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + var bytesProperties = GetStringProperties(payload.Body.Body.Properties); + var numberProperties = GetNumberProperties(payload.Body.Body.Properties); + + var birthday = GetBirthday(Encoding.UTF8.GetString(bytesProperties[20031])); + var reg = DateTime.UnixEpoch.AddSeconds(numberProperties[20026]); + + string? qid = Encoding.UTF8.GetString(bytesProperties[27394]); + + uint statusId = numberProperties[27372]; + uint mask = 268435455 - statusId; + mask = (uint)((int)mask >> 31); + statusId -= 268435456 & mask; + + var customs = bytesProperties[27406].Length != 0 + ? Serializer.Deserialize(new MemoryStream(bytesProperties[27406])) + : null; + + var avatars = Serializer.Deserialize(new MemoryStream(bytesProperties[101])); + + var business = Serializer.Deserialize(new MemoryStream(bytesProperties[107])); + var businessA = (business.Body?.Lists ?? new()) + .Select(b => new BusinessCustom + { + Type = b.Type, + Level = b.Level, + Icon = b.Icon, + IsPro = b.IsPro, + IsYear = b.IsYear + }) + .ToList(); + + string? nickname = Encoding.UTF8.GetString(bytesProperties[20002]); + string? city = Encoding.UTF8.GetString(bytesProperties[20020]); + string? country = Encoding.UTF8.GetString(bytesProperties[20003]); + string? school = Encoding.UTF8.GetString(bytesProperties[20021]); + string? sign = Encoding.UTF8.GetString(bytesProperties[102]); + + var info = new BotUserInfo( + payload.Body.Body.Uin, + nickname, + $"{avatars.Url}640", + birthday, + city, + country, + school, + numberProperties[20037], + reg, + (GenderInfo)numberProperties[20009], + qid, + numberProperties[105], + sign, + new() + { + StatusId = statusId, + FaceId = customs?.FaceId ?? 0, + Msg = customs?.Msg + }, + businessA + ); + + output = FetchUserInfoEvent.Result(0, info); + extraEvents = null; + return true; + } + + private static DateTime GetBirthday(string birthday) + { + var bin = new BinaryPacket(Encoding.ASCII.GetBytes(birthday)); + var year = bin.ReadUshort(); + var month = bin.ReadByte(); + var day = bin.ReadByte(); + if (year != 0 && month is >= 1 and <= 12 && day >= 1 && day <= DateTime.DaysInMonth(year, month)) + { + return new DateTime(year, month, day); + } + return new DateTime(1970, 1, 1); + } + + private static Dictionary GetStringProperties(OidbSvcTrpcTcp0xFE1_2ResponseProperty properties) + { + var result = new Dictionary(); + foreach (var property in properties.BytesProperties) + { + result[property.Code] = property.Value; + } + + return result; + } + + private static Dictionary GetNumberProperties(OidbSvcTrpcTcp0xFE1_2ResponseProperty properties) + { + var result = new Dictionary(); + foreach (var property in properties.NumberProperties) + { + result[property.Number1] = property.Number2; + } + + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/HighwayUrlService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/HighwayUrlService.cs new file mode 100644 index 0000000..5718d3f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/HighwayUrlService.cs @@ -0,0 +1,63 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Action.HttpConn; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(HighwayUrlEvent))] +[Service("HttpConn.0x6ff_501")] +internal class HighwayUrlService : BaseService +{ + protected override bool Build(HighwayUrlEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new HttpConn0x6ff_501 + { + HttpConn = new HttpConn + { + Field1 = 0, + Field2 = 0, + Field3 = 16, + Field4 = 1, + Tgt = keystore.Session.Tgt.Hex().ToLower(), + Field6 = 3, + ServiceTypes = new List { 1, 5, 10, 21 }, + Field9 = 2, + Field10 = 9, + Field11 = 8, + Ver = "1.0.1" + } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out HighwayUrlEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize(input); + + var servers = new Dictionary>(); + foreach (var serverInfo in packet.HttpConn.ServerInfos) + { + uint type = serverInfo.ServiceType; + servers[type] = new List(); + + foreach (var serverAddr in serverInfo.ServerAddrs) + { + var ip = BitConverter.GetBytes(serverAddr.Ip); + servers[type].Add(new Uri($"http://{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}:{serverAddr.Port}/cgi-bin/httpconn?htcmd=0x6FF0087&uin={keystore.Uin}")); + } + } + + output = HighwayUrlEvent.Result(0, packet.HttpConn.SigSession, servers); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/InfoSyncService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/InfoSyncService.cs new file mode 100644 index 0000000..12b1a33 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/InfoSyncService.cs @@ -0,0 +1,92 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(InfoSyncEvent))] +[Service("trpc.msg.register_proxy.RegisterProxy.SsoInfoSync")] +internal class InfoSyncService : BaseService +{ + protected override bool Build(InfoSyncEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new SsoInfoSyncRequest + { + SyncFlag = 735, + ReqRandom = (uint)Random.Shared.Next(), + CurActiveStatus = 2, + GroupLastMsgTime = 0, + C2CInfoSync = new SsoC2CInfoSync + { + C2CMsgCookie = new SsoC2CMsgCookie + { + C2CLastMsgTime = 0 + }, + C2CLastMsgTime = 0, + LastC2CMsgCookie = new SsoC2CMsgCookie + { + C2CLastMsgTime = 0 + } + }, + NormalConfig = new NormalConfig + { + IntCfg = new Dictionary() + }, + RegisterInfo = new RegisterInfo + { + Guid = device.Guid.ToByteArray().Hex(true), + KickPC = 0, + CurrentVersion = appInfo.CurrentVersion, + IsFirstRegisterProxyOnline = 1, + LocaleId = 2052, + Device = new OnlineDeviceInfo() + { + User = device.DeviceName, + Os = appInfo.Kernel, + OsVer = device.SystemKernel, + VendorName = "", + OsLower = appInfo.VendorOs, + }, + SetMute = 0, + RegisterVendorType = 6, + RegType = 0, + BusinessInfo = new OnlineBusinessInfo + { + NotifySwitch = 1, + BindUinNotifySwitch = 1 + }, + BatteryStatus = 0, + Field12 = 1 + }, + UnknownStructure = new UnknownStructure + { + GroupCode = 0, + Flag2 = 2 + }, + AppState = new CurAppState + { + IsDelayRequest = 0, + AppStatus = 0, + SilenceStatus = 0 + } + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out InfoSyncEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize(input); + + output = InfoSyncEvent.Result(response?.RegisterInfoResponse?.Message ?? "IDK"); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/KickNTService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/KickNTService.cs new file mode 100644 index 0000000..795576c --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/KickNTService.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.System; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +// ReSharper disable once InconsistentNaming + +[EventSubscribe(typeof(KickNTEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.KickNT")] +internal class KickNTService : BaseService +{ + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out KickNTEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize(input); + output = KickNTEvent.Create(response.Tips, response.Title); + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetFriendRequestService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetFriendRequestService.cs new file mode 100644 index 0000000..2d3d732 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetFriendRequestService.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(SetFriendRequestEvent))] +[Service("OidbSvcTrpcTcp.0xb5d_44")] +internal class SetFriendRequestService : BaseService +{ + protected override bool Build(SetFriendRequestEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xB5D_44 + { + Accept = input.Accept ? 3u : 5u, + TargetUid = input.TargetUid + }); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetFriendRequestEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + + output = SetFriendRequestEvent.Result((int)packet.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetNeedToConfirmSwitchService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetNeedToConfirmSwitchService.cs new file mode 100644 index 0000000..3b36014 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SetNeedToConfirmSwitchService.cs @@ -0,0 +1,47 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(SetNeedToConfirmSwitchEvent))] +[Service("OidbSvcTrpcTcp.0x1277_0")] +internal class SetNeedToConfirmSwitchService : BaseService +{ + protected override bool Build(SetNeedToConfirmSwitchEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x1277_0 + { + Body = new OidbSvcTrpcTcp0x1277_0Body + { + Device = new OidbSvcTrpcTcp0x1277_0Device + { + Guid = device.Guid.ToByteArray(), + AppId = (uint)appInfo.AppId, + PackageName = appInfo.PackageName + }, + GuidEncryptedType = false, + AutoLogin = input.EnableNoNeed + } + }, false, true); + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SetNeedToConfirmSwitchEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + output = SetNeedToConfirmSwitchEvent.Result((int)payload.ErrorCode); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/SsoAliveService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SsoAliveService.cs new file mode 100644 index 0000000..14c8f21 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/SsoAliveService.cs @@ -0,0 +1,31 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(SsoAliveEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat")] +internal class SsoAliveService : BaseService +{ + protected override bool Build(SsoAliveEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + using var stream = new MemoryStream(); + var packet = new NTSsoHeartBeat { Type = 1 }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SsoAliveEvent output, out List? extraEvents) + { + output = SsoAliveEvent.Result(); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Internal/Service/System/StatusRegisterService.cs b/Lagrange.Core/Lagrange.Core/Internal/Service/System/StatusRegisterService.cs new file mode 100644 index 0000000..7fe6f08 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Internal/Service/System/StatusRegisterService.cs @@ -0,0 +1,51 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.System; +using Lagrange.Core.Internal.Packets.System; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.System; + +[EventSubscribe(typeof(StatusRegisterEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.Register")] +internal class StatusRegisterService : BaseService +{ + protected override bool Build(StatusRegisterEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new RegisterInfo + { + Guid = device.Guid.ToByteArray().Hex(true), + KickPC = 0, + CurrentVersion = appInfo.CurrentVersion, + IsFirstRegisterProxyOnline = 0, + LocaleId = 2052, + Device = new OnlineDeviceInfo + { + User = device.DeviceName, + Os = appInfo.Kernel, + OsVer = device.SystemKernel, + VendorName = "", + OsLower = appInfo.VendorOs, + }, + SetMute = 0, + RegisterVendorType = 0, + RegType = 1, + }; + + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out StatusRegisterEvent output, out List? extraEvents) + { + var response = Serializer.Deserialize(input); + + output = StatusRegisterEvent.Result(response.Message ?? "OK"); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Lagrange.Core.csproj b/Lagrange.Core/Lagrange.Core/Lagrange.Core.csproj new file mode 100644 index 0000000..ace5e9f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Lagrange.Core.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + enable + true + Lagrange.Core + Linwenxuan04, Tiger0132, LXY1226, TheSnowfield, and KonataDev/Contributors + The Implementation of NTQQ for Pure C#, Event Driven, derived from Konata.Core + 10 + true + 0.3.1 + Linwenxuan04 2023 + https://github.com/LagrangeDev/Lagrange.Core + git + true + README.md + GPL-3.0-or-later + + + + + + + + diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/BounceFaceEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/BounceFaceEntity.cs new file mode 100644 index 0000000..b8cd02f --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/BounceFaceEntity.cs @@ -0,0 +1,112 @@ +using System.Text; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(CommonElem))] +public class BounceFaceEntity : IMessageEntity +{ + public uint FaceId { get; set; } + + public uint Count { get; set; } + + public string Name { get; set; } + + public bool ShouldAddPreviewText { get; set; } + + public BounceFaceEntity() : this(0, 0, string.Empty) {} + + public BounceFaceEntity(SysFaceEntry face, uint count, bool shouldAddPreviewText = true) + { + if (!face.AniStickerId.HasValue) + throw new ArgumentException("Face does not have a sticker ID", nameof(face)); + + FaceId = (uint)face.AniStickerId.Value; + Count = count; + Name = face.QDes ?? string.Empty; + ShouldAddPreviewText = shouldAddPreviewText; + + // Because the name is used as a preview text, it should not start with '/' + // But the QDes of the face may start with '/', so remove it + if (Name.StartsWith('/')) + Name = Name[1..]; + } + + public BounceFaceEntity(uint faceId, uint count, string? name = null, bool shouldAddPreviewText = true) + { + FaceId = faceId; + Count = count; + + // If the name is null, it will be assigned in MessagingLogic.ResolveOutgoingChain + Name = name!; + ShouldAddPreviewText = shouldAddPreviewText; + } + + IEnumerable IMessageEntity.PackElement() + { + byte[] pbElem; + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, new QBounceFaceExtra + { + Field1 = 13, + Face = new QSmallFaceExtra + { + FaceId = FaceId, + Text = Name, + CompatText = Name + }, + Count = Count, + Name = Name + }); + pbElem = ms.ToArray(); + } + + var common = new CommonElem + { + ServiceType = 23, + PbElem = pbElem, + BusinessType = 13 + }; + + if (!ShouldAddPreviewText) + return new Elem[] { new() { CommonElem = common } }; + + byte[] textFallbackPb; + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, new QBounceFaceExtra.FallbackPreviewTextPb { Text = $"[{Name}]请使用最新版手机QQ体验新功能。" }); + textFallbackPb = ms.ToArray(); + } + return new Elem[] + { + new() { CommonElem = common }, + new() + { + Text = new Text + { + Str = ToPreviewText(), + PbReserve = textFallbackPb + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elem) + { + if (elem.CommonElem is not { ServiceType: 23 } common) + return null; + + var extra = Serializer.Deserialize(common.PbElem.AsSpan()); + return new BounceFaceEntity(extra.Face.FaceId, extra.Count, extra.Name); + } + + public string ToPreviewString() => "$[BounceFace | Name: {Name}({FaceId}) | Count: {Count}]"; + + public string ToPreviewText() => $"[{Name}]x{Count}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/FaceEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/FaceEntity.cs new file mode 100644 index 0000000..c3e3512 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/FaceEntity.cs @@ -0,0 +1,110 @@ +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(Face)), MessageElement(typeof(CommonElem))] +public class FaceEntity : IMessageEntity +{ + public ushort FaceId { get; set; } + + public bool IsLargeFace { get; set; } + + public SysFaceEntry? SysFaceEntry { get; set; } + + public FaceEntity() { } + + public FaceEntity(ushort faceId, bool isLargeFace) + { + FaceId = faceId; + IsLargeFace = isLargeFace; + } + + IEnumerable IMessageEntity.PackElement() + { + if (IsLargeFace) + { + var qBigFace = new QBigFaceExtra + { + AniStickerPackId = SysFaceEntry?.AniStickerPackId.ToString() ?? "1", + AniStickerId = SysFaceEntry?.AniStickerId.ToString() ?? "8", + FaceId = FaceId, + Field4 = 1, + AniStickerType = SysFaceEntry?.AniStickerType ?? 1, + Field6 = "", + Preview = SysFaceEntry?.QDes ?? "", + Field9 = 1 + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, qBigFace); + return new Elem[] + { + new() + { + CommonElem = new CommonElem + { + ServiceType = 37, + PbElem = stream.ToArray(), + BusinessType = (uint)(SysFaceEntry?.AniStickerType ?? 1) + } + } + }; + } + + if (FaceId >= 260) + { + var qSmallFace = new QSmallFaceExtra + { + FaceId = FaceId, + Text = SysFaceEntry?.QDes ?? "", + CompatText = SysFaceEntry?.QDes ?? "" + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, qSmallFace); + return new Elem[] + { + new() + { + CommonElem = new CommonElem + { + ServiceType = 33, + PbElem = stream.ToArray(), + BusinessType = (uint)(SysFaceEntry?.AniStickerType ?? 1) + } + } + }; + } + + return new Elem[] { new() { Face = new Face { Index = FaceId } } }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.Face is { Old: not null } face) + { + ushort? faceId = (ushort?)face.Index; + if (faceId != null) return new FaceEntity((ushort)faceId, false); + } + + if (elems.CommonElem is { ServiceType:37, PbElem: not null } common) + { + var qFace = Serializer.Deserialize(common.PbElem.AsSpan()); + + ushort? faceId = (ushort?)qFace.FaceId; + if (faceId != null) return new FaceEntity((ushort)faceId, true); + } + + if (elems.CommonElem is { ServiceType: 33, PbElem: not null } append) + { + var qSmallFace = Serializer.Deserialize(append.PbElem.AsSpan()); + return new FaceEntity((ushort)qSmallFace.FaceId, false); + } + + return null; + } + + public string ToPreviewString() => $"[Face][{(IsLargeFace ? "Large" : "Small")}]: {FaceId}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/FileEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/FileEntity.cs new file mode 100644 index 0000000..c7b8b66 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/FileEntity.cs @@ -0,0 +1,120 @@ +using Lagrange.Core.Internal.Packets.Message.Component; +using Lagrange.Core.Internal.Packets.Message.Component.Extra; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(TransElem))] +public class FileEntity : IMessageEntity +{ + public long FileSize { get; internal set; } + + public string FileName { get; internal set; } + + public byte[] FileMd5 { get; internal set; } + + public string? FileUrl { get; internal set; } + + /// + /// Only Group File has such field + /// + public string? FileId { get; set; } + + public string? FileUuid { get; set; } + + public string? FileHash { get; set; } + + internal Stream? FileStream { get; set; } + + internal byte[] FileSha1 { get; set; } + + public FileEntity() + { + FileName = ""; + FileMd5 = Array.Empty(); + FileSha1 = Array.Empty(); + } + + /// + /// This entity could not be directly sent via , + /// it should be sent via + /// + public FileEntity(string path) + { + FileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + FileMd5 = FileStream.Md5().UnHex(); + FileSize = FileStream.Length; + FileName = Path.GetFileName(path); + FileSha1 = FileStream.Sha1().UnHex(); + } + + /// + /// This entity could not be directly sent via , + /// it should be sent via + /// + public FileEntity(byte[] payload, string fileName) + { + FileStream = new MemoryStream(payload); + FileMd5 = payload.Md5().UnHex(); + FileSize = payload.Length; + FileName = fileName; + FileSha1 = FileStream.Sha1().UnHex(); + } + + internal FileEntity(long fileSize, string fileName, byte[] fileMd5, string fileUuid, string fileHash) + { + FileSize = fileSize; + FileName = fileName; + FileMd5 = fileMd5; + FileUuid = fileUuid; + FileHash = fileHash; + FileSha1 = Array.Empty(); + } + + IEnumerable IMessageEntity.PackElement() => Array.Empty(); + + object IMessageEntity.PackMessageContent() => new FileExtra + { + File = new NotOnlineFile + { + FileType = 0, + FileUuid = FileUuid, + FileMd5 = FileMd5, + FileName = FileName, + FileSize = FileSize, + Subcmd = 1, + DangerEvel = 0, + ExpireTime = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0) + TimeSpan.FromDays(7)).TotalSeconds, + FileHash = FileHash + } + }; + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.TransElem is { ElemType: 24 } trans) + { + var payload = new BinaryPacket(trans.ElemValue); + payload.Skip(1); + var data = payload.ReadBytes(Prefix.Uint16 | Prefix.LengthOnly); + var extra = Serializer.Deserialize(data).Inner.Info; + + return new FileEntity + { + FileSize = extra.FileSize, + FileMd5 = extra.FileMd5.UnHex(), + FileId = extra.FileId, + FileName = extra.FileName + }; + } + + return null; + } + + public string ToPreviewString() => $"[File] {FileName} ({FileSize}): {FileUrl ?? "failed to receive file url"}"; + + public string ToPreviewText() => $"[文件] {FileName}"; +} diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/ForwardEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/ForwardEntity.cs new file mode 100644 index 0000000..81ca5df --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/ForwardEntity.cs @@ -0,0 +1,111 @@ +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(SrcMsg))] +public class ForwardEntity : IMessageEntity +{ + public DateTime Time { get; set; } + + public ulong MessageId { get; set; } + + public uint Sequence { get; set; } + + public uint ClientSequence { get; set; } + + public string? Uid { get; set; } + + public uint TargetUin { get; set; } + + internal List Elements { get; } + + private string? SelfUid { get; set; } + + public ForwardEntity() + { + Sequence = 0; + Uid = null; + Elements = new List(); + } + + public ForwardEntity(MessageChain chain) + { + Time = chain.Time; + Sequence = chain.Sequence; + ClientSequence = chain.ClientSequence; + Uid = chain.Uid; + Elements = chain.Elements; + TargetUin = chain.FriendUin; + MessageId = chain.MessageId; + } + + IEnumerable IMessageEntity.PackElement() + { + var forwardReserve = new SrcMsg.Preserve + { + MessageId = MessageId, + }; + using var forwardStream = new MemoryStream(); + Serializer.Serialize(forwardStream, forwardReserve); + + var mentionReserve = new MentionExtra + { + Type = TargetUin == 0 ? 1 : 2, + Uin = 0, + Field5 = 0, + Uid = Uid, + }; + using var mentionStream = new MemoryStream(); + Serializer.Serialize(mentionStream, mentionReserve); + + return new Elem[] + { + new() + { + SrcMsg = new SrcMsg + { + OrigSeqs = new List { ClientSequence != 0 ? ClientSequence : Sequence }, + SenderUin = TargetUin, // Can't get self uin + Time = (int)new DateTimeOffset(Time).ToUnixTimeSeconds(), + Elems = Elements, + PbReserve = forwardStream.ToArray(), + ToUin = 0 + } + }, + new() + { + Text = ClientSequence == 0 ? new Text + { + Str = "not null", + PbReserve = mentionStream.ToArray() + } : null + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.SrcMsg is { } srcMsg) + { + var reserve = Serializer.Deserialize(srcMsg.PbReserve.AsSpan()); + return new ForwardEntity + { + Time = DateTimeOffset.FromUnixTimeSeconds(srcMsg.Time ?? 0).LocalDateTime, + Sequence = reserve.FriendSequence ?? srcMsg.OrigSeqs?[0] ?? 0, + TargetUin = (uint)srcMsg.SenderUin, + MessageId = reserve.MessageId + }; + } + + return null; + } + + public void SetSelfUid(string selfUid) => SelfUid = selfUid; + + public string ToPreviewString() => $"[Forward] Time: {Time} Sequence: {Sequence} "; + + public string ToPreviewText() => string.Empty; +} diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/GreyTipEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/GreyTipEntity.cs new file mode 100644 index 0000000..ee9c4c7 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/GreyTipEntity.cs @@ -0,0 +1,68 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Lagrange.Core.Internal.Packets.Message.Component.Extra; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Message.Entity; + +public class GreyTipEntity : IMessageEntity +{ + public string GreyTip { get; } + + public GreyTipEntity(string greyTip) => GreyTip = greyTip; + + public GreyTipEntity() => GreyTip = string.Empty; + + IEnumerable IMessageEntity.PackElement() + { + var content = new GreyTipContent + { + GrayTip = GreyTip, + ObjectType = 3, + SubType = 2, + Type = 4 + }; + + var extra = new GreyTipExtra + { + Layer1 = new GreyTipExtraLayer1 + { + Info = new GreyTipExtraInfo { Content = JsonSerializer.Serialize(content), Type = 1 } + } + }; + + return new Elem[] + { + new() + { + GeneralFlags = new GeneralFlags + { + PbReserve = extra.Serialize().ToArray(), + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elem) + { + throw new NotImplementedException(); + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } + + private class GreyTipContent + { + [JsonPropertyName("gray_tip")] public string GrayTip { get; set; } = string.Empty; + + [JsonPropertyName("object_type")] public uint ObjectType { get; set; } + + [JsonPropertyName("sub_type")] public uint SubType { get; set; } + + [JsonPropertyName("type")] public uint Type { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/GroupReactionEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/GroupReactionEntity.cs new file mode 100644 index 0000000..4944a44 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/GroupReactionEntity.cs @@ -0,0 +1,67 @@ +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation.Extra; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(CommonElem))] +public class GroupReactionEntity : IMessageEntity +{ + private readonly IEnumerable _reactions; + + public GroupReactionEntity() { _reactions = Array.Empty(); } + + public GroupReactionEntity(IEnumerable reactions) + { + _reactions = reactions; + } + + IEnumerable IMessageEntity.PackElement() + { + var infos = _reactions + .Select(reaction => new GroupReactionExtraFaceInfo() + { + FaceId = reaction.FaceId, + Type = reaction.Type, + Count = reaction.Count, + IsAdded = reaction.IsAdded ? 1u : 0u, + }) + .ToArray(); + + var extra = new GroupReactionExtra + { + Body = new() + { + Field1 = new() + { + Field1 = 0, + Field2 = 7240 + }, + FaceInfos = infos, + } + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, extra); + + return new Elem[] { + new() { CommonElem = new() { + ServiceType = 38, + PbElem = stream.ToArray(), + BusinessType = 1 + } } + }; + } + + string IMessageEntity.ToPreviewString() + { + return ""; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elem) + { + // TODO Parase + return null; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/ImageEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/ImageEntity.cs new file mode 100644 index 0000000..7ca2920 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/ImageEntity.cs @@ -0,0 +1,193 @@ +using System.Numerics; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(NotOnlineImage))] +[MessageElement(typeof(CustomFace))] +[MessageElement(typeof(CommonElem))] +public class ImageEntity : IMessageEntity +{ + private const string BaseUrl = "https://multimedia.nt.qq.com.cn"; + + private const string LegacyBaseUrl = "http://gchat.qpic.cn"; + + public Vector2 PictureSize { get; set; } + + public string FilePath { get; set; } = string.Empty; + + public byte[] ImageMd5 { get; set; } = Array.Empty(); + + public uint ImageSize { get; set; } + + public string ImageUrl { get; set; } = string.Empty; + + internal Lazy? ImageStream { get; set; } + + internal MsgInfo? MsgInfo { get; set; } + + internal NotOnlineImage? CompatImage { get; set; } + + internal CustomFace? CompatFace { get; set; } + + internal string? Summary { get; set; } + + public int SubType { get; set; } + + internal bool IsGroup { get; set; } + + public ImageEntity() { } + + public ImageEntity(string filePath) + { + FilePath = filePath; + ImageStream = new Lazy(() => new FileStream(filePath, FileMode.Open, FileAccess.Read)); + } + + public ImageEntity(byte[] file) + { + FilePath = ""; + ImageStream = new Lazy(() => new MemoryStream(file)); + } + + public ImageEntity(Stream stream) + { + FilePath = ""; + ImageStream = new Lazy(stream); + } + + IEnumerable IMessageEntity.PackElement() + { + bool isGroup = MsgInfo?.MsgInfoBody is { Count: > 0 } body && body[0].HashSum.TroopSource?.GroupUin != null; + + var common = MsgInfo.Serialize(); + + var elems = new Elem[] + { + new(), + new() + { + CommonElem = new CommonElem + { + ServiceType = 48, + PbElem = common.ToArray(), + BusinessType = isGroup ? 20u : 10u, + } + } + }; + + if (CompatFace != null) elems[0].CustomFace = CompatFace; + if (CompatImage != null) elems[0].NotOnlineImage = CompatImage; + + return elems; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.CommonElem is { ServiceType: 48, BusinessType: 20 or 10 } common) + { + var extra = Serializer.Deserialize(common.PbElem.AsSpan()); + var msgInfoBody = extra.MsgInfoBody[0]; + var index = msgInfoBody.Index; + + return new ImageEntity + { + PictureSize = new Vector2(index.Info.Width, index.Info.Height), + FilePath = index.Info.FileName, + ImageMd5 = index.Info.FileHash.UnHex(), + ImageSize = index.Info.FileSize, + MsgInfo = extra, + SubType = (int)extra.ExtBizInfo.Pic.BizType, + IsGroup = extra.ExtBizInfo.Pic.BytesPbReserveTroop != null, + Summary = string.IsNullOrEmpty(extra.ExtBizInfo.Pic.TextSummary) ? "[图片]" : extra.ExtBizInfo.Pic.TextSummary, + }; + } + + if (elems.NotOnlineImage is { } image) + { + if (image.OrigUrl.Contains("&fileid=")) // NTQQ's shit + { + return new ImageEntity // NTQQ Mobile + { + PictureSize = new Vector2(image.PicWidth, image.PicHeight), + FilePath = image.FilePath, + ImageMd5 = image.PicMd5, + ImageSize = image.FileLen, + ImageUrl = $"{BaseUrl}{image.OrigUrl}", + Summary = image.PbRes.Summary, + SubType = image.PbRes.SubType, + IsGroup = false + }; + } + + return new ImageEntity + { + PictureSize = new Vector2(image.PicWidth, image.PicHeight), + FilePath = image.FilePath, + ImageMd5 = image.PicMd5, + ImageSize = image.FileLen, + ImageUrl = $"{LegacyBaseUrl}{image.OrigUrl}", + Summary = image.PbRes.Summary, + SubType = image.PbRes.SubType, + IsGroup = false + }; + } + + if (elems.CustomFace is { } face) + { + if (face.OrigUrl.Contains("&fileid=")) + { + return new ImageEntity // NTQQ Mobile + { + PictureSize = new Vector2(face.Width, face.Height), + FilePath = face.FilePath, + ImageMd5 = face.Md5, + ImageSize = face.Size, + ImageUrl = $"{BaseUrl}{face.OrigUrl}", + Summary = face.PbReserve?.Summary, + SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face), + IsGroup = true + }; + } + + return new ImageEntity + { + PictureSize = new Vector2(face.Width, face.Height), + FilePath = face.FilePath, + ImageMd5 = face.Md5, + ImageSize = face.Size, + ImageUrl = $"{LegacyBaseUrl}{face.OrigUrl}", + Summary = face.PbReserve?.Summary, + SubType = face.PbReserve?.SubType ?? GetImageTypeFromFaceOldData(face), + IsGroup = true + }; + } + + return null; + } + + private static int GetImageTypeFromFaceOldData(CustomFace face) + { + if (face.OldData is not { Length: >= 5 }) return 0; // maybe legacy PCQQ(TIM) + + return face.OldData[4].ToString("X2") switch + { + "36" => 1, + _ => 0, + }; + } + + public string ToPreviewString() => $"[Image: {PictureSize.X}x{PictureSize.Y}] {ToPreviewText()} {FilePath} {ImageSize} {ImageUrl}"; + + public string ToPreviewText() => string.IsNullOrEmpty(Summary) + ? SubType switch + { + 1 => "[动画表情]", + _ => "[图片]", + } + : Summary; +} diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/JsonEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/JsonEntity.cs new file mode 100644 index 0000000..1ba6a61 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/JsonEntity.cs @@ -0,0 +1,68 @@ +using System.Text; +using System.Text.Json.Nodes; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Binary.Compression; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(RichMsg))] +public class JsonEntity : IMessageEntity +{ + public string Json { get; set; } + + public string ResId { get; set; } + + public JsonEntity() + { + Json = ""; + ResId = ""; + } + + public JsonEntity(string json, string resId = "") + { + Json = json; + ResId = resId; + } + + public JsonEntity(JsonNode json, string resId = "") + { + Json = json.ToJsonString(); + ResId = resId; + } + + IEnumerable IMessageEntity.PackElement() + { + return new Elem[] + { + new() + { + Text = new Text { Str = ResId } + }, + new() + { + RichMsg = new RichMsg + { + ServiceId = 1, + Template1 = ZCompression.ZCompress(Json, new byte[] { 0x01 }), + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.RichMsg is { ServiceId: 1, Template1: not null } richMsg) + { + var json = ZCompression.ZDecompress(richMsg.Template1[1..]); + return new XmlEntity(Encoding.UTF8.GetString(json)); + } + + return null; + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core/Message/Entity/KeyboardEntity.cs b/Lagrange.Core/Lagrange.Core/Message/Entity/KeyboardEntity.cs new file mode 100644 index 0000000..b38c225 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core/Message/Entity/KeyboardEntity.cs @@ -0,0 +1,119 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Lagrange.Core.Internal.Packets.Message.Component.Extra; +using Lagrange.Core.Internal.Packets.Message.Element; +using Lagrange.Core.Internal.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +public class KeyboardEntity : IMessageEntity +{ + public KeyboardData Data { get; set; } + + internal KeyboardEntity() => Data = new KeyboardData(); + + public KeyboardEntity(KeyboardData data) => Data = data; + + public KeyboardEntity(string data) => Data = JsonSerializer.Deserialize(data) ?? throw new Exception(); + + IEnumerable IMessageEntity.PackElement() => new Elem[] + { + new() + { + CommonElem = new CommonElem + { + ServiceType = 46, + PbElem = new ButtonExtra { Data = Data }.Serialize().ToArray(), + BusinessType = 1 + } + } + }; + + IMessageEntity? IMessageEntity.UnpackElement(Elem elem) => null; + + public string ToPreviewString() => throw new NotImplementedException(); +} + +# region Json & Protobuf + +[ProtoContract] +public class KeyboardData +{ + [JsonPropertyName("rows")] [ProtoMember(1)] + public List Rows { get; set; } = new(); +} + +[ProtoContract] +public class Row +{ + [JsonPropertyName("buttons")] [ProtoMember(1)] + public List