├── .DS_Store ├── .github ├── .codecov.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yaml │ ├── config.yml │ ├── documentation.md │ ├── feature-request.yaml │ ├── other.yml │ └── rfc.md └── workflows │ ├── auto-invite-comment.yml │ ├── changelog.yml │ ├── check-coverage.yml │ ├── cleanup-after-milestone-prs-merged.yml │ ├── codeql-analysis.yml │ ├── comment-check.yml │ ├── delete-users-invalid-comments.yml │ ├── go-build-test.yml │ ├── issue-translator.yml │ ├── merge-from-milestone.yml │ ├── release.yml │ ├── remove-unused-labels.yml │ ├── reopen-issue.yml │ └── update-version-file-on-release.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── README_zh-CN.md ├── docs ├── .generated_docs ├── CHANGELOG.md ├── CODEOWNERS ├── contrib │ ├── cicd-actions.md │ ├── code_conventions.md │ ├── development.md │ └── git_workflow.md └── gomobile-android-ios-setup-cn.md ├── go.mod ├── go.sum ├── identifier.sqlite ├── integration_test ├── README.md ├── internal │ ├── checker │ │ ├── common.go │ │ ├── conversation.go │ │ ├── counter.go │ │ ├── group.go │ │ ├── msg.go │ │ ├── relation.go │ │ └── user.go │ ├── config │ │ ├── config.go │ │ ├── limit.go │ │ └── statistics.go │ ├── manager │ │ ├── file_manager.go │ │ ├── group_manager.go │ │ ├── manager.go │ │ ├── msg_manager.go │ │ ├── relation_manager.go │ │ └── user_manager.go │ ├── pkg │ │ ├── decorator │ │ │ ├── log.go │ │ │ └── log_test.go │ │ ├── initialization │ │ │ ├── flag.go │ │ │ ├── global.go │ │ │ ├── log.go │ │ │ └── sdk.go │ │ ├── progress │ │ │ ├── ansi.go │ │ │ ├── api.go │ │ │ ├── bar.go │ │ │ └── progress.go │ │ ├── reerrgroup │ │ │ └── errgroup.go │ │ ├── sdk_user_simulator │ │ │ ├── listener.go │ │ │ └── user.go │ │ └── utils │ │ │ ├── context.go │ │ │ ├── err_test.go │ │ │ ├── error.go │ │ │ ├── group.go │ │ │ └── user.go │ ├── process │ │ ├── process.go │ │ ├── process_helper.go │ │ └── task.go │ ├── sdk │ │ ├── conversation.go │ │ ├── group.go │ │ ├── msg.go │ │ ├── relation.go │ │ └── sdk.go │ ├── statistics │ │ └── msg.go │ └── vars │ │ ├── cmd.go │ │ ├── group.go │ │ ├── msg.go │ │ ├── os.go │ │ └── user.go └── main.go ├── internal ├── conversation_msg │ ├── api.go │ ├── conversation.go │ ├── conversation_msg.go │ ├── conversion.go │ ├── create_message.go │ ├── delete.go │ ├── entering.go │ ├── image.go │ ├── incremental_sync.go │ ├── max_seq_recorder.go │ ├── message_check.go │ ├── message_check_test.go │ ├── notification.go │ ├── progress.go │ ├── read_drawing.go │ ├── revoke.go │ ├── server_api.go │ ├── skipList.go │ └── sync.go ├── flagconst │ └── flag.go ├── group │ ├── api.go │ ├── cache.go │ ├── conversion.go │ ├── full_sync.go │ ├── group.go │ ├── incremental_sync.go │ ├── notification.go │ └── server_api.go ├── interaction │ ├── compressor.go │ ├── constant.go │ ├── context.go │ ├── encoder.go │ ├── long_conn_mgr.go │ ├── long_connection.go │ ├── msg_sync.go │ ├── online.go │ ├── reconnect.go │ ├── subscription.go │ ├── subscription_test.go │ ├── ws_default.go │ ├── ws_js.go │ └── ws_resp_asyn.go ├── relation │ ├── api.go │ ├── conversion.go │ ├── incremental_sync.go │ ├── notification.go │ ├── relation.go │ ├── server_api.go │ └── sync.go ├── third │ ├── api.go │ ├── file │ │ ├── bitmap.go │ │ ├── cb.go │ │ ├── file.go │ │ ├── file_default.go │ │ ├── file_js.go │ │ ├── file_test.go │ │ ├── md5.go │ │ ├── progress.go │ │ └── upload.go │ ├── log.go │ ├── log_test.go │ ├── progress.go │ ├── third.go │ └── zip.go └── user │ ├── api.go │ ├── conversion.go │ ├── full_sync.go │ ├── notification.go │ ├── server_api.go │ └── user.go ├── msgtest ├── config.go ├── conversation_test.go ├── main │ └── main.go ├── message_test.go ├── module │ ├── api_msg_sender.go │ ├── friend_manager.go │ ├── group_manager.go │ ├── manager.go │ ├── msg_sender.go │ ├── pressure.go │ └── user_manager.go └── sdk_user_simulator │ ├── listener.go │ └── user.go ├── open_im_sdk ├── apicb.go ├── caller.go ├── conversation_msg.go ├── em.go ├── group.go ├── init_login.go ├── listener.go ├── online.go ├── relation.go ├── third.go ├── user.go └── userRelated.go ├── open_im_sdk_callback ├── callback_client.go └── callback_go_sdk.go ├── pkg ├── api │ ├── api.go │ └── fn.go ├── cache │ ├── cache.go │ ├── conversation_seq_cache.go │ └── user_cache.go ├── ccontext │ ├── context.go │ └── context_test.go ├── cliconf │ ├── client_config.go │ └── global.go ├── common │ ├── EventQueue.go │ ├── EventQueue_test.go │ ├── priorityQueue.go │ ├── priorityQueue_test.go │ └── trigger_channel.go ├── constant │ └── constant.go ├── content_type │ └── content_type.go ├── datafetcher │ └── datafetcher.go ├── db │ ├── admin_group_request_model.go │ ├── app_version.go │ ├── app_version_test.go │ ├── black_model.go │ ├── chat_log_model.go │ ├── chat_log_model_test.go │ ├── conversation_model.go │ ├── conversation_unread_message_model.go │ ├── db_init.go │ ├── db_interface │ │ └── databse.go │ ├── db_js.go │ ├── friend_model.go │ ├── friend_model_test.go │ ├── friend_request_model.go │ ├── group_member_model.go │ ├── group_model.go │ ├── group_model_test.go │ ├── group_request_model.go │ ├── model_struct │ │ └── data_model_struct.go │ ├── notification_model.go │ ├── notification_model_test.go │ ├── sending_messages_model.go │ ├── seq_data_model.go │ ├── table_master.go │ ├── upload_model.go │ ├── user_model.go │ ├── version_sync.go │ └── version_sync_test.go ├── network │ ├── http_client.go │ ├── http_client_test.go │ └── new_http.go ├── page │ └── pagereq.go ├── sdk_params_callback │ ├── conversation_msg_sdk_struct.go │ ├── friend_sdk_struct.go │ └── group_sdk_struct.go ├── sdkerrs │ ├── code.go │ ├── error.go │ └── predefine.go ├── server_api_params │ ├── conversation_api_struct.go │ └── friend_api_struct.go ├── sort_conversation │ └── sort_conversation.go ├── syncer │ ├── state.go │ ├── syncer.go │ └── version_synchronizer.go ├── utils │ ├── file.go │ ├── lock_pool.go │ └── utils.go └── version │ ├── README.md │ ├── base.go │ ├── helpers.go │ ├── helpers_test.go │ ├── types.go │ └── version.go ├── scripts └── template │ ├── LICENSE │ ├── footer.md.tmpl │ └── head.md.tmpl ├── sdk_struct └── sdk_struct.go ├── test ├── callback.go ├── config.go ├── conversation_test.go ├── create_msg_test.go ├── empty_test.go ├── file_test.go ├── friend_test.go ├── group_test.go ├── init.go ├── listener.go ├── long_conn_test.go ├── third_test.go └── user_test.go ├── tools └── changelog │ └── changelog.go ├── version ├── version └── version.go └── wasm ├── cmd ├── Makefile ├── main.go └── static │ └── wasm_exec.js ├── event_listener ├── callback_writer.go ├── caller.go └── listener.go ├── exec └── executor.go ├── indexdb ├── app_version.go ├── black_model.go ├── chat_log_model.go ├── conversation_model.go ├── conversation_unread_message_model.go ├── friend_model.go ├── friend_request_model.go ├── group_member_model.go ├── group_model.go ├── group_request.model.go ├── init.go ├── notification_model.go ├── sending_messages_model.go ├── table_master.go ├── temp_struct │ └── struct.go ├── upload_model.go ├── user_model.go └── version_sync.go └── wasm_wrapper ├── wasm_conversation_msg.go ├── wasm_friend.go ├── wasm_group.go ├── wasm_init_login.go ├── wasm_signaling.go ├── wasm_third.go └── wasm_user.go /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openimsdk/openim-sdk-core/04b02fe43fbaa491be5a018cc27983168cdd8d24/.DS_Store -------------------------------------------------------------------------------- /.github/.codecov.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM SDK. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | coverage: 16 | status: 17 | project: 18 | default: false # disable the default status that measures entire project 19 | pkg: # declare a new status context "pkg" 20 | paths: 21 | - pkg/* # only include coverage in "pkg/" folder 22 | informational: true # Always pass check 23 | patch: off # disable the commit only checks -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | # - name: "Bug Report" 4 | # description: "Report a bug in the project" 5 | # file: "bug-report.yml" 6 | - name: 📢 Connect on slack 7 | url: https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A 8 | about: Support OpenIM-related requests or issues, get in touch with developers and help on slack 9 | - name: 🌐 OpenIM Blog 10 | url: https://www.openim.io/ 11 | about: Open the OpenIM community blog 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation Update 3 | about: Propose updates to documentation, including README files and other docs. 4 | title: "[DOC]: " # Prefix for the title to help identify documentation issues 5 | labels: documentation # Labels to be automatically added 6 | assignees: '' # Optionally, specify maintainers or teams to be auto-assigned 7 | 8 | --- 9 | 10 | ## Documentation Updates 11 | Describe the documentation that needs to be updated or corrected. Please specify the files and sections if possible. 12 | 13 | ## Motivation 14 | Explain why these updates are necessary. What is missing, misleading, or outdated? 15 | 16 | ## Suggested Changes 17 | Detail the changes that you propose. If you are suggesting large changes, include examples or mockups of what the updated documentation should look like. 18 | 19 | ## Additional Information 20 | Include any other information that might be relevant, such as links to discussions or related issues in the repository. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | title: "[FEATURE REQUEST] " 3 | labels: ["feature request","enhancement"] 4 | description: "Propose a new feature or improvement that you believe will help enhance the project." 5 | # assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: "Thank you for taking the time to propose a feature request. Please fill in as much detail as possible to help us understand why this feature is necessary and how it should work." 11 | 12 | - type: textarea 13 | id: feature-reason 14 | attributes: 15 | label: Why this feature? 16 | description: "Explain why this feature is needed. What problem does it solve? How does it benefit the project and its users?" 17 | placeholder: "Describe the need for this feature..." 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: solution-proposal 23 | attributes: 24 | label: Suggested Solution 25 | description: "Describe your proposed solution for this feature. How do you envision it working?" 26 | placeholder: "Detail your solution here..." 27 | validations: 28 | required: true 29 | 30 | - type: markdown 31 | attributes: 32 | value: "Please provide any other relevant information or screenshots that could help illustrate your idea." 33 | 34 | - type: textarea 35 | id: additional-info 36 | attributes: 37 | label: Additional Information 38 | description: "Include any additional information, links, or screenshots that might be relevant to your feature request." 39 | placeholder: "Add more context or links to relevant resources..." 40 | 41 | - type: markdown 42 | attributes: 43 | value: "Thank you for contributing to the project! We appreciate your input and will review your suggestion as soon as possible." 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.yml: -------------------------------------------------------------------------------- 1 | name: 🐧 Other 2 | description: Use this for any other issues. Please do NOT create blank issues 3 | title: "[Other]: " 4 | labels: ["other"] 5 | # assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: "# Other issue" 11 | - type: textarea 12 | id: issuedescription 13 | attributes: 14 | label: What would you like to share? 15 | description: Provide a clear and concise explanation of your issue. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: extrainfo 20 | attributes: 21 | label: Additional information 22 | description: Is there anything else we should know about this issue? 23 | validations: 24 | required: false 25 | - type: markdown 26 | attributes: 27 | value: | 28 | You can also join our Discord community [here](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) 29 | Feel free to check out other cool repositories of the openim Community [here](https://github.com/openimsdk) 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/rfc.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: RFC - Feature Proposal 3 | about: Submit a proposal for a significant feature to invite community discussion. 4 | title: "[RFC]: " # Prefix for the title to help identify RFC proposals 5 | labels: rfc, proposal # Labels to be automatically added 6 | assignees: '' # Optionally, specify maintainers or teams to be auto-assigned 7 | 8 | --- 9 | 10 | ## Proposal Overview 11 | Briefly describe the content and objectives of your proposal. 12 | 13 | ## Motivation 14 | Why is this new feature necessary? What is the background of this problem? 15 | 16 | ## Detailed Design 17 | Describe the technical details of the proposal, including implementation steps, code snippets, or architecture diagrams. 18 | 19 | ## Alternatives Considered 20 | Have other alternatives been considered? Why is this approach preferred over others? 21 | 22 | ## Impact 23 | How will this proposal affect existing practices and community users? 24 | 25 | ## Additional Information 26 | Include any other relevant information such as related discussions, prior related work, etc. 27 | -------------------------------------------------------------------------------- /.github/workflows/check-coverage.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM SDK. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # name: Check-Coverage 16 | 17 | 18 | # on: 19 | # workflow_dispatch: 20 | # push: 21 | # branches: [ "main" ] 22 | # paths-ignore: 23 | # - "docs/**" 24 | # - "**/*.md" 25 | # - "**/*.yaml" 26 | # - "CONTRIBUTORS" 27 | # - "CHANGELOG/**" 28 | # pull_request: 29 | # branches: [ "*" ] 30 | # paths-ignore: 31 | # - "docs/**" 32 | # - "**/*.md" 33 | # - "**/*.yaml" 34 | # - "CONTRIBUTORS" 35 | # - "CHANGELOG/**" 36 | # env: 37 | # # Common versions 38 | # GO_VERSION: "1.20" 39 | 40 | # jobs: 41 | # coverage: 42 | # runs-on: ubuntu-20.04 43 | # steps: 44 | # - name: Checkout 45 | # uses: actions/checkout@v3 46 | 47 | # - name: Setup Golang with cache 48 | # uses: magnetikonline/action-golang-cache@v3 49 | # with: 50 | # go-version: ${{ env.GO_VERSION }} 51 | # token: ${{ secrets.BOT_TOKEN }} 52 | 53 | # - name: Install Dependencies 54 | # run: sudo apt update && sudo apt install -y libgpgme-dev libbtrfs-dev libdevmapper-dev 55 | 56 | # - name: Run Cover 57 | # run: make cover 58 | 59 | # - name: Upload Coverage to Codecov 60 | # uses: codecov/codecov-action@v3 61 | -------------------------------------------------------------------------------- /.github/workflows/cleanup-after-milestone-prs-merged.yml: -------------------------------------------------------------------------------- 1 | name: Cleanup After Milestone PRs Merged 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - closed 7 | 8 | jobs: 9 | handle_pr: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4.2.0 15 | 16 | - name: Get the PR title and extract PR numbers 17 | id: extract_pr_numbers 18 | run: | 19 | # Get the PR title 20 | PR_TITLE="${{ github.event.pull_request.title }}" 21 | 22 | echo "PR Title: $PR_TITLE" 23 | 24 | # Extract PR numbers from the title 25 | PR_NUMBERS=$(echo "$PR_TITLE" | grep -oE "#[0-9]+" | tr -d '#' | tr '\n' ' ') 26 | echo "Extracted PR Numbers: $PR_NUMBERS" 27 | 28 | # Save PR numbers to a file 29 | echo "$PR_NUMBERS" > pr_numbers.txt 30 | echo "Saved PR Numbers to pr_numbers.txt" 31 | 32 | # Check if the title matches the specific pattern 33 | if echo "$PR_TITLE" | grep -qE "\[Created by @.+ from #[0-9]+\]$"; then 34 | echo "proceed=true" >> $GITHUB_OUTPUT 35 | else 36 | echo "proceed=false" >> $GITHUB_OUTPUT 37 | fi 38 | 39 | - name: Use extracted PR numbers and label PRs 40 | if: (steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge')) && github.event.pull_request.merged == true 41 | run: | 42 | # Read the previously saved PR numbers 43 | PR_NUMBERS=$(cat pr_numbers.txt) 44 | echo "Using extracted PR Numbers: $PR_NUMBERS" 45 | 46 | # Loop through each PR number and add label 47 | for PR_NUMBER in $PR_NUMBERS; do 48 | echo "Adding 'cherry-picked' label to PR #$PR_NUMBER" 49 | curl -X POST \ 50 | -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 51 | -H "Accept: application/vnd.github+json" \ 52 | https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/labels \ 53 | -d '{"labels":["cherry-picked"]}' 54 | done 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | - name: Delete branch after PR close 59 | if: steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge') || contains(github.event.pull_request.labels.*.name, 'changelog') 60 | continue-on-error: true 61 | run: | 62 | BRANCH_NAME="${{ github.event.pull_request.head.ref }}" 63 | echo "Branch to delete: $BRANCH_NAME" 64 | git push origin --delete "$BRANCH_NAME" 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '18 19 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'go' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v4 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v3 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v3 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v3 -------------------------------------------------------------------------------- /.github/workflows/issue-translator.yml: -------------------------------------------------------------------------------- 1 | name: 'issue-translator' 2 | on: 3 | issue_comment: 4 | types: [created] 5 | issues: 6 | types: [opened] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: usthe/issues-translate-action@v2.7 13 | with: 14 | BOT_GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} 15 | IS_MODIFY_TITLE: true 16 | # not require, default false, . Decide whether to modify the issue title 17 | # if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot. 18 | CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿 19 | # not require. Customize the translation robot prefix message. -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: OpenIM release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | permissions: 9 | contents: write 10 | packages: write 11 | issues: write 12 | 13 | jobs: 14 | goreleaser: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | - run: git fetch --force --tags 21 | - uses: actions/setup-go@v4 22 | with: 23 | go-version: stable 24 | # More assembly might be required: Docker logins, GPG, etc. It all depends 25 | # on your needs. 26 | - uses: goreleaser/goreleaser-action@v4 27 | with: 28 | distribution: goreleaser 29 | version: latest 30 | workdir: . 31 | args: release --clean 32 | env: 33 | USERNAME: ${{ github.repository_owner }} 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/remove-unused-labels.yml: -------------------------------------------------------------------------------- 1 | name: Remove Unused Labels 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | cleanup: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | contents: read 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Fetch All Issues and PRs 17 | id: fetch_issues_prs 18 | uses: actions/github-script@v7.0.1 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | script: | 22 | const issues = await github.paginate(github.rest.issues.listForRepo, { 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | state: 'all', 26 | per_page: 100 27 | }); 28 | 29 | const labelsInUse = new Set(); 30 | issues.forEach(issue => { 31 | issue.labels.forEach(label => { 32 | labelsInUse.add(label.name); 33 | }); 34 | }); 35 | 36 | return JSON.stringify(Array.from(labelsInUse)); 37 | result-encoding: string 38 | 39 | - name: Fetch All Labels 40 | id: fetch_labels 41 | uses: actions/github-script@v7.0.1 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | script: | 45 | const labels = await github.paginate(github.rest.issues.listLabelsForRepo, { 46 | owner: context.repo.owner, 47 | repo: context.repo.repo, 48 | per_page: 100 49 | }); 50 | 51 | return JSON.stringify(labels.map(label => label.name)); 52 | result-encoding: string 53 | 54 | - name: Remove Unused Labels 55 | uses: actions/github-script@v7.0.1 56 | with: 57 | github-token: ${{ secrets.GITHUB_TOKEN }} 58 | script: | 59 | const labelsInUse = new Set(JSON.parse(process.env.LABELS_IN_USE)); 60 | const allLabels = JSON.parse(process.env.ALL_LABELS); 61 | 62 | const unusedLabels = allLabels.filter(label => !labelsInUse.has(label)); 63 | 64 | for (const label of unusedLabels) { 65 | await github.rest.issues.deleteLabel({ 66 | owner: context.repo.owner, 67 | repo: context.repo.repo, 68 | name: label 69 | }); 70 | console.log(`Deleted label: ${label}`); 71 | } 72 | 73 | env: 74 | LABELS_IN_USE: ${{ steps.fetch_issues_prs.outputs.result }} 75 | ALL_LABELS: ${{ steps.fetch_labels.outputs.result }} -------------------------------------------------------------------------------- /docs/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @openimsdk/go-code-review 2 | -------------------------------------------------------------------------------- /docs/contrib/code_conventions.md: -------------------------------------------------------------------------------- 1 | # Code conventions 2 | 3 | - [Code conventions](#code-conventions) 4 | - [POSIX shell](#posix-shell) 5 | - [Go](#go) 6 | - [Directory and file conventions](#directory-and-file-conventions) 7 | - [Testing conventions](#testing-conventions) 8 | 9 | ## POSIX shell 10 | 11 | - [Style guide](https://google.github.io/styleguide/shell.xml) 12 | 13 | ## Go 14 | 15 | - [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) 16 | - [Effective Go](https://golang.org/doc/effective_go.html) 17 | - Know and avoid [Go landmines](https://gist.github.com/lavalamp/4bd23295a9f32706a48f) 18 | - Comment your code. 19 | - [Go's commenting conventions](http://blog.golang.org/godoc-documenting-go-code) 20 | - If reviewers ask questions about why the code is the way it is, that's a sign that comments might be helpful. 21 | - Command-line flags should use dashes, not underscores 22 | - Naming 23 | - Please consider package name when selecting an interface name, and avoid redundancy. For example, `storage.Interface` is better than `storage.StorageInterface`. 24 | - Do not use uppercase characters, underscores, or dashes in package names. 25 | - Please consider parent directory name when choosing a package name. For example, `pkg/controllers/autoscaler/foo.go` should say `package autoscaler` not `package autoscalercontroller`. 26 | - Unless there's a good reason, the `package foo` line should match the name of the directory in which the `.go` file exists. 27 | - Importers can use a different name if they need to disambiguate. 28 | 29 | ## Directory and file conventions 30 | 31 | - Avoid general utility packages. Packages called "util" are suspect. Instead, derive a name that describes your desired function. For example, the utility functions dealing with waiting for operations are in the `wait` package and include functionality like `Poll`. The full name is `wait.Poll`. 32 | - All filenames should be lowercase. 33 | - All source files and directories should use underscores, not dashes. 34 | - Package directories should generally avoid using separators as much as possible. When package names are multiple words, they usually should be in nested subdirectories. 35 | 36 | ## Testing conventions 37 | 38 | Please refer to [TESTING.md](../../tests/TESTING.md) document. 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/openimsdk/openim-sdk-core/v3 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.0 6 | 7 | require ( 8 | github.com/coder/websocket v1.8.13 9 | github.com/golang/protobuf v1.5.4 10 | github.com/gorilla/websocket v1.4.2 11 | github.com/jinzhu/copier v0.4.0 12 | github.com/pkg/errors v0.9.1 13 | google.golang.org/protobuf v1.35.1 // indirect 14 | gorm.io/driver/sqlite v1.5.5 15 | ) 16 | 17 | require golang.org/x/net v0.39.0 // indirect 18 | 19 | require ( 20 | github.com/google/go-cmp v0.6.0 21 | github.com/openimsdk/protocol v0.0.73-alpha.6 22 | github.com/openimsdk/tools v0.0.50-alpha.80 23 | github.com/patrickmn/go-cache v2.1.0+incompatible 24 | github.com/stretchr/testify v1.9.0 25 | golang.org/x/image v0.26.0 26 | golang.org/x/sync v0.13.0 27 | gorm.io/gorm v1.25.10 28 | ) 29 | 30 | require ( 31 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 32 | github.com/jinzhu/inflection v1.0.0 // indirect 33 | github.com/jinzhu/now v1.1.5 // indirect 34 | github.com/lestrrat-go/strftime v1.0.6 // indirect 35 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 36 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 37 | go.uber.org/atomic v1.7.0 // indirect 38 | go.uber.org/multierr v1.6.0 // indirect 39 | go.uber.org/zap v1.24.0 // indirect 40 | golang.org/x/sys v0.32.0 // indirect 41 | golang.org/x/text v0.24.0 // indirect 42 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect 43 | google.golang.org/grpc v1.68.0 // indirect 44 | gopkg.in/yaml.v3 v3.0.1 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /identifier.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openimsdk/openim-sdk-core/04b02fe43fbaa491be5a018cc27983168cdd8d24/identifier.sqlite -------------------------------------------------------------------------------- /integration_test/README.md: -------------------------------------------------------------------------------- 1 | **Package dependency relationship** 2 | 3 | ``` 4 | process -> manager -> sdk_user_simulator -> vars -> sdkserver 5 | sdk -> vars 6 | ``` -------------------------------------------------------------------------------- /integration_test/internal/checker/common.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/tools/log" 6 | ) 7 | 8 | const ( 9 | errChanLen = 10 10 | ) 11 | 12 | var ( 13 | checkErrChan = make(chan error, errChanLen) 14 | ) 15 | 16 | func InsertToErrChan(ctx context.Context, err error) { 17 | select { 18 | case checkErrChan <- err: 19 | default: 20 | log.ZDebug(ctx, "checkErrChan is full") 21 | } 22 | } 23 | 24 | func CloseAndGetCheckErrChan() <-chan error { 25 | close(checkErrChan) 26 | return checkErrChan 27 | } 28 | -------------------------------------------------------------------------------- /integration_test/internal/checker/conversation.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk" 8 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 9 | ) 10 | 11 | // CheckConvNumAfterImpFriAndCrGro check conversation num after import friends and create groups. 12 | func CheckConvNumAfterImpFriAndCrGro(ctx context.Context) error { 13 | corrects := func() [2]int { 14 | // corrects[0] :super user conversion num 15 | // corrects[1] :common user conversion num 16 | largeNum := vars.LargeGroupNum 17 | commonNum := 0 // cal by userNum 18 | groupNum := largeNum + commonNum 19 | 20 | superNum := vars.UserNum - 1 + groupNum 21 | commonUserNum := vars.SuperUserNum + groupNum 22 | 23 | return [2]int{superNum, commonUserNum} 24 | }() 25 | 26 | c := &CounterChecker[*sdk.TestSDK, string]{ 27 | CheckName: "checkConversationNum", 28 | CheckerKeyName: "userID", 29 | GoroutineLimit: config.ErrGroupCommonLimit, 30 | GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) { 31 | totalNum, err := t.GetTotalConversationCount(ctx) 32 | if err != nil { 33 | return 0, err 34 | } 35 | return totalNum, nil 36 | }, 37 | CalCorrectCount: func(userID string) int { 38 | commonGroupNum := calCommonGroup(utils.MustGetUserNum(userID)) 39 | if utils.IsSuperUser(userID) { 40 | return corrects[0] + commonGroupNum 41 | } else { 42 | return corrects[1] + commonGroupNum 43 | } 44 | }, 45 | LoopSlice: sdk.TestSDKs, 46 | GetKey: func(t *sdk.TestSDK) string { 47 | return t.UserID 48 | }, 49 | } 50 | 51 | return c.LoopCheck(ctx) 52 | } 53 | -------------------------------------------------------------------------------- /integration_test/internal/checker/group.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk" 8 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 9 | ) 10 | 11 | func CheckGroupNum(ctx context.Context) error { 12 | correct := func() int { 13 | largeNum := vars.LargeGroupNum 14 | commonNum := 0 // cal by userNum 15 | return largeNum + commonNum 16 | }() 17 | 18 | c := &CounterChecker[*sdk.TestSDK, string]{ 19 | CheckName: "checkGroupNum", 20 | CheckerKeyName: "userID", 21 | GoroutineLimit: config.ErrGroupCommonLimit, 22 | GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) { 23 | _, groupNum, err := t.GetAllJoinedGroups(ctx) 24 | if err != nil { 25 | return 0, err 26 | } 27 | return groupNum, nil 28 | }, 29 | CalCorrectCount: func(userID string) int { 30 | return correct + calCommonGroup(utils.MustGetUserNum(userID)) 31 | }, 32 | LoopSlice: sdk.TestSDKs, 33 | GetKey: func(t *sdk.TestSDK) string { 34 | return t.UserID 35 | }, 36 | } 37 | 38 | return c.LoopCheck(ctx) 39 | } 40 | 41 | func calCommonGroup(userNum int) int { 42 | 43 | preNum := utils.NextOffsetNum(userNum, -(vars.CommonGroupMemberNum - 1)) 44 | 45 | createNum := 0 46 | for i := 0; i < vars.CommonGroupMemberNum; i++ { 47 | if utils.IsNumLogin(preNum) { 48 | createNum++ 49 | } 50 | preNum = utils.NextNum(preNum) 51 | } 52 | return createNum * vars.CommonGroupNum 53 | } 54 | -------------------------------------------------------------------------------- /integration_test/internal/checker/relation.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk" 8 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 9 | ) 10 | 11 | // CheckLoginUsersFriends check login users friends 12 | func CheckLoginUsersFriends(ctx context.Context) error { 13 | corrects := func() [2]int { 14 | // corrects[0] :super user friend num 15 | // corrects[1] :common user friend num 16 | 17 | return [2]int{vars.UserNum - 1, vars.SuperUserNum} 18 | }() 19 | 20 | c := &CounterChecker[*sdk.TestSDK, string]{ 21 | CheckName: "checkLoginUsersFriends", 22 | CheckerKeyName: "userID", 23 | GoroutineLimit: config.ErrGroupCommonLimit, 24 | GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) { 25 | friendList, err := t.GetAllFriends(ctx) 26 | if err != nil { 27 | return 0, err 28 | } 29 | return len(friendList), nil 30 | }, 31 | CalCorrectCount: func(userID string) int { 32 | if utils.IsSuperUser(userID) { 33 | return corrects[0] 34 | } else { 35 | return corrects[1] 36 | } 37 | }, 38 | LoopSlice: sdk.TestSDKs[:vars.LoginUserNum], 39 | GetKey: func(t *sdk.TestSDK) string { 40 | return t.UserID 41 | }, 42 | } 43 | 44 | return c.LoopCheck(ctx) 45 | } 46 | -------------------------------------------------------------------------------- /integration_test/internal/checker/user.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 7 | ) 8 | 9 | func CheckLoginByRateNum(ctx context.Context) error { 10 | correct := func() int { 11 | return vars.LoginUserNum 12 | }() 13 | 14 | c := &CounterChecker[int, string]{ 15 | CheckName: "checkLoginByRateNum", 16 | CheckerKeyName: "loginNum", 17 | GoroutineLimit: config.ErrGroupCommonLimit, 18 | GetTotalCount: func(ctx context.Context, t int) (int, error) { 19 | return int(vars.NowLoginNum.Load()), nil 20 | }, 21 | CalCorrectCount: func(_ string) int { 22 | return correct 23 | }, 24 | LoopSlice: []int{0}, 25 | GetKey: func(t int) string { 26 | return "login" 27 | }, 28 | } 29 | 30 | return c.LoopCheck(ctx) 31 | } 32 | 33 | // CheckAllLoginNum check if all user is login 34 | func CheckAllLoginNum(ctx context.Context) error { 35 | correct := func() int { 36 | return vars.UserNum 37 | }() 38 | 39 | c := &CounterChecker[int, string]{ 40 | CheckName: "checkLoginByRateNum", 41 | CheckerKeyName: "loginNum", 42 | GoroutineLimit: config.ErrGroupCommonLimit, 43 | GetTotalCount: func(ctx context.Context, t int) (int, error) { 44 | return int(vars.NowLoginNum.Load()), nil 45 | }, 46 | CalCorrectCount: func(_ string) int { 47 | return correct 48 | }, 49 | LoopSlice: []int{0}, 50 | GetKey: func(t int) string { 51 | return "login" 52 | }, 53 | } 54 | 55 | return c.LoopCheck(ctx) 56 | } 57 | -------------------------------------------------------------------------------- /integration_test/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 5 | "github.com/openimsdk/protocol/constant" 6 | ) 7 | 8 | const ( 9 | TestIP = "127.0.0.1" 10 | APIAddr = "http://" + TestIP + ":10002" 11 | WsAddr = "ws://" + TestIP + ":10001" 12 | AdminUserID = "imAdmin" 13 | Secret = "123456" 14 | PlatformID = constant.AndroidPlatformID 15 | LogLevel = 3 16 | DataDir = "./data/" 17 | LogFilePath = "./logs/" 18 | IsLogStandardOutput = false 19 | ) 20 | 21 | func GetConf() sdk_struct.IMConfig { 22 | var cf sdk_struct.IMConfig 23 | cf.ApiAddr = APIAddr 24 | cf.WsAddr = WsAddr 25 | cf.DataDir = DataDir 26 | cf.LogLevel = LogLevel 27 | cf.PlatformID = int32(PlatformID) 28 | cf.LogFilePath = LogFilePath 29 | cf.IsLogStandardOutput = IsLogStandardOutput 30 | return cf 31 | } 32 | -------------------------------------------------------------------------------- /integration_test/internal/config/limit.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | MaxUserNum = 1e+5 // max users 5 | ) 6 | 7 | const ( 8 | ErrGroupSmallLimit = 20 // max goroutine of a small error group 9 | ErrGroupMiddleSmallLimit = 50 // max goroutine of a middle small error group 10 | ErrGroupCommonLimit = 100 // max goroutine of a common error group 11 | ) 12 | 13 | const ( 14 | SleepSec = 30 15 | CheckWaitSec = 5 // check wait sec 16 | BarRemoveWaiteSec = 1 // progress bar remove wait second 17 | ) 18 | 19 | const ( 20 | CheckMsgRate = 1 // Sampling and statistical message ratio. Max check message is MaxCheckMsg 21 | MaxCheckMsg = 1e+8 22 | MaxCheckLoopNum = 40 23 | ) 24 | 25 | const ( 26 | ApiParamLength = 1000 27 | ) 28 | -------------------------------------------------------------------------------- /integration_test/internal/config/statistics.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | ReceiveMsgTimeThresholdLow = 1 // Receive msg time threshold low. Unit: s 5 | ReceiveMsgTimeThresholdMedium = 3 // Receive msg time threshold medium. Unit: s 6 | ReceiveMsgTimeThresholdHigh = 5 // Receive msg time threshold high. Unit: s 7 | ) 8 | -------------------------------------------------------------------------------- /integration_test/internal/manager/file_manager.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator" 7 | "github.com/openimsdk/tools/errs" 8 | "os" 9 | ) 10 | 11 | type TestFileManager struct { 12 | *MetaManager 13 | } 14 | 15 | func NewFileManager(m *MetaManager) *TestFileManager { 16 | return &TestFileManager{m} 17 | } 18 | 19 | func (m *TestFileManager) DeleteLocalDB(ctx context.Context) error { 20 | defer decorator.FuncLog(ctx)() 21 | 22 | conf := config.GetConf() 23 | err := os.RemoveAll(conf.DataDir) 24 | if err != nil { 25 | return errs.WrapMsg(err, "remove db failed") 26 | } 27 | err = os.MkdirAll(conf.DataDir, os.ModePerm) 28 | if err != nil { 29 | return errs.WrapMsg(err, "make db dir failed") 30 | } 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /integration_test/internal/manager/manager.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext" 9 | "github.com/openimsdk/openim-sdk-core/v3/pkg/network" 10 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 11 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 12 | authPB "github.com/openimsdk/protocol/auth" 13 | "github.com/openimsdk/tools/mcontext" 14 | ) 15 | 16 | type MetaManager struct { 17 | sdk_struct.IMConfig 18 | secret string 19 | token string 20 | } 21 | 22 | func NewMetaManager() *MetaManager { 23 | conf := config.GetConf() 24 | return &MetaManager{ 25 | IMConfig: conf, 26 | secret: config.Secret, 27 | } 28 | } 29 | 30 | func (m *MetaManager) ApiPost(ctx context.Context, route string, req, resp any) (err error) { 31 | return network.ApiPost(ctx, route, req, resp) 32 | } 33 | 34 | // PostWithCtx should only be used for scenarios such as registration and login that do not require a token or 35 | // require an admin token. 36 | // For scenarios that require a specific user token, please obtain the context from vars.Contexts for the request. 37 | func (m *MetaManager) PostWithCtx(route string, req, resp any) error { 38 | return m.ApiPost(m.BuildCtx(nil), route, req, resp) 39 | } 40 | 41 | // BuildCtx build an admin token 42 | func (m *MetaManager) BuildCtx(ctx context.Context) context.Context { 43 | if ctx == nil { 44 | ctx = context.Background() 45 | } 46 | ctx = ccontext.WithInfo(ctx, &ccontext.GlobalConfig{ 47 | Token: m.token, 48 | IMConfig: &m.IMConfig, 49 | }) 50 | ctx = ccontext.WithOperationID(ctx, utils.OperationIDGenerator()) 51 | ctx = mcontext.SetOpUserID(ctx, "admin") 52 | return ctx 53 | } 54 | 55 | func (m *MetaManager) GetSecret() string { 56 | return m.secret 57 | } 58 | 59 | func (m *MetaManager) GetAdminToken(userID string, platformID int32) (string, error) { 60 | req := authPB.GetAdminTokenReq{UserID: userID, Secret: m.secret} 61 | resp := authPB.GetAdminTokenResp{} 62 | err := m.PostWithCtx(api.GetAdminToken.Route(), &req, &resp) 63 | if err != nil { 64 | return "", err 65 | } 66 | return resp.Token, nil 67 | } 68 | 69 | func (m *MetaManager) GetUserToken(userID string, platformID int32) (string, error) { 70 | req := authPB.GetUserTokenReq{PlatformID: platformID, UserID: userID} 71 | resp := authPB.GetUserTokenResp{} 72 | err := m.PostWithCtx(api.GetUsersToken.Route(), &req, &resp) 73 | if err != nil { 74 | return "", err 75 | } 76 | return resp.Token, nil 77 | } 78 | 79 | func (m *MetaManager) WithAdminToken() (err error) { 80 | token, err := m.GetAdminToken(config.AdminUserID, config.PlatformID) 81 | if err != nil { 82 | return err 83 | } 84 | m.token = token 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /integration_test/internal/manager/relation_manager.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress" 8 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup" 9 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 10 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 11 | "github.com/openimsdk/protocol/relation" 12 | "github.com/openimsdk/tools/log" 13 | ) 14 | 15 | type TestRelationManager struct { 16 | *MetaManager 17 | } 18 | 19 | func NewRelationManager(m *MetaManager) *TestRelationManager { 20 | return &TestRelationManager{m} 21 | } 22 | 23 | // ImportFriends Import all users as friends of the superuser (excluding themselves), 24 | // making the superuser have all users as friends, 25 | // while regular users have the superuser as their only friend. 26 | // A superuser is defined as a user who has all users as friends, 27 | // their IDs range from 0 to vars.SuperUserNum. 28 | func (m *TestRelationManager) ImportFriends(ctx context.Context) error { 29 | defer decorator.FuncLog(ctx)() 30 | 31 | gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupMiddleSmallLimit) 32 | 33 | var ( 34 | total int 35 | now int 36 | ) 37 | total = vars.SuperUserNum 38 | progress.FuncNameBarPrint(cctx, gr, now, total) 39 | for i, userID := range vars.SuperUserIDs { 40 | i := i 41 | userID := userID 42 | gr.Go(func() error { 43 | friendIDs := vars.UserIDs[i+1:] // excluding oneself 44 | if len(friendIDs) == 0 { 45 | return nil 46 | } 47 | 48 | for i := 0; i < len(friendIDs); i += config.ApiParamLength { 49 | end := i + config.ApiParamLength 50 | if end > len(friendIDs) { 51 | end = len(friendIDs) 52 | } 53 | req := &relation.ImportFriendReq{ 54 | OwnerUserID: userID, 55 | FriendUserIDs: friendIDs[i:end], 56 | } 57 | ctx := m.BuildCtx(ctx) 58 | log.ZWarn(ctx, "ImportFriends begin", nil, "len", len(friendIDs)) 59 | if err := api.ImportFriendList.Execute(ctx, req); err != nil { 60 | return err 61 | } 62 | log.ZWarn(ctx, "ImportFriends end", nil, "len", len(friendIDs)) 63 | } 64 | return nil 65 | }) 66 | } 67 | return gr.Wait() 68 | } 69 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/decorator/log.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/openimsdk/tools/log" 7 | "github.com/openimsdk/tools/utils/stringutil" 8 | "time" 9 | ) 10 | 11 | // FuncLog is a log print decorator. 12 | // Correct usage is: defer decorator.FuncLog(ctx)() 13 | func FuncLog(ctx context.Context) func() { 14 | return FuncLogSkip(ctx, 1) 15 | } 16 | 17 | // FuncLogSkip is a log print decorator. The argument skip is the number of stack frames 18 | // to ascend. 19 | // e.g. 20 | // 21 | // func FuncName(ctx context.Context){ 22 | // middleFunc(ctx) 23 | // } 24 | // 25 | // func middleFunc(ctx context.Context){ 26 | // FuncLogSkip(ctx, 1) 27 | // // ... 28 | // } 29 | // 30 | // the funcName is `FuncName` 31 | func FuncLogSkip(ctx context.Context, skip int) func() { 32 | funcName := stringutil.GetFuncName(skip + 1) // +1 is FuncLogSkip 33 | return ProgressLog(ctx, funcName) 34 | } 35 | 36 | func ProgressLog(ctx context.Context, name string) func() { 37 | t := time.Now() 38 | log.ZInfo(ctx, fmt.Sprintf("%s begin", name)) 39 | fmt.Println(fmt.Sprintf("%s begin", name)) 40 | return func() { 41 | log.ZInfo(ctx, fmt.Sprintf("%s end", name), "time consuming", time.Since(t)) 42 | fmt.Println(fmt.Sprintf("%s end. Time consuming: %v", name, time.Since(t))) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/decorator/log_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestFuncLog(t *testing.T) { 10 | FuncName(context.Background()) 11 | } 12 | 13 | func FuncName(ctx context.Context) { 14 | middleFunc(ctx) 15 | } 16 | 17 | func middleFunc(ctx context.Context) { 18 | defer FuncLogSkip(ctx, 1)() 19 | //... 20 | fmt.Println("middle func") 21 | } 22 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/initialization/global.go: -------------------------------------------------------------------------------- 1 | package initialization 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 8 | "math" 9 | "math/rand" 10 | "time" 11 | ) 12 | 13 | func InitGlobalData() { 14 | sdk.TestSDKs = make([]*sdk.TestSDK, vars.UserNum) 15 | vars.Contexts = make([]context.Context, vars.UserNum) 16 | vars.Cancels = make([]context.CancelFunc, vars.UserNum) 17 | vars.LoginUserNum = int(math.Floor(vars.LoginRate * float64(vars.UserNum))) 18 | vars.RecvMsgConsuming = make(chan *vars.StatMsg, config.MaxCheckMsg) 19 | rand.New(rand.NewSource(time.Now().UnixNano())) 20 | } 21 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/initialization/log.go: -------------------------------------------------------------------------------- 1 | package initialization 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 7 | "github.com/openimsdk/openim-sdk-core/v3/version" 8 | "github.com/openimsdk/protocol/constant" 9 | "github.com/openimsdk/tools/log" 10 | ) 11 | 12 | const ( 13 | rotateCount uint = 1 14 | rotationTime uint = 24 15 | ) 16 | 17 | func InitLog(cf sdk_struct.IMConfig) error { 18 | if err := log.InitLoggerFromConfig("open-im-sdk-core", "", cf.SystemType, constant.PlatformID2Name[int(cf.PlatformID)], int(cf.LogLevel), cf.IsLogStandardOutput, false, cf.LogFilePath, rotateCount, rotationTime, version.Version, true); err != nil { 19 | fmt.Println("log init failed ", err.Error()) 20 | return err 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/initialization/sdk.go: -------------------------------------------------------------------------------- 1 | package initialization 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/manager" 5 | ) 6 | 7 | func GenUserIDs() { 8 | manager.NewUserManager(manager.NewMetaManager()).GenAllUserIDs() 9 | } 10 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/progress/ansi.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import "fmt" 4 | 5 | // esc generates multiple ANSI control characters 6 | func esc(suffix ...string) (ansis string) { 7 | for _, s := range suffix { 8 | ansis += fmt.Sprintf("%c[%s", 033, s) 9 | } 10 | return 11 | } 12 | 13 | func clearLine() string { 14 | return esc("2K") 15 | } 16 | 17 | func cursorHorizontalAbsolute(n int) string { 18 | return esc(fmt.Sprintf("%dG", n)) 19 | } 20 | 21 | func moveToLineHead() string { 22 | return cursorHorizontalAbsolute(1) 23 | } 24 | 25 | func cursorUp(n int) string { 26 | return esc(fmt.Sprintf("%dA", n)) 27 | } 28 | 29 | // cursorUpHead up and move to head 30 | func cursorUpHead(n int) string { 31 | return esc(fmt.Sprintf("%dF", n)) 32 | } 33 | 34 | func saveCursor() string { 35 | return esc("s") 36 | } 37 | 38 | func loadCursor() string { 39 | return esc("u") 40 | } 41 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/progress/api.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup" 6 | "github.com/openimsdk/tools/utils/stringutil" 7 | ) 8 | 9 | func FuncNameBarPrint(ctx context.Context, gr *reerrgroup.Group, now, total int) *Progress { 10 | return FuncBarPrint(ctx, stringutil.GetFuncName(1), gr, now, total) 11 | } 12 | 13 | func FuncBarPrint(ctx context.Context, name string, gr *reerrgroup.Group, now, total int) *Progress { 14 | bar := NewBar(name, now, total, false) 15 | p := Start(bar) 16 | gr.SetAfterTasks(func() error { 17 | p.IncBar(bar) 18 | return nil 19 | }) 20 | 21 | go func() { 22 | select { 23 | case <-ctx.Done(): 24 | p.Stop() 25 | case <-p.done: 26 | return // p is done 27 | } 28 | }() 29 | 30 | return p 31 | } 32 | 33 | func Start(bar ...*Bar) *Progress { 34 | return StartWithMode(AutoClose|ForbiddenWrite, bar...) 35 | } 36 | 37 | func StartWithMode(mode proFlag, bar ...*Bar) *Progress { 38 | p := NewProgress(mode, 0) 39 | p.Start() 40 | 41 | p.AddBar(bar...) 42 | return p 43 | } 44 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/progress/bar.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 5 | "time" 6 | ) 7 | 8 | func NewBar(name string, now, total int, ifRemove bool) *Bar { 9 | return &Bar{ 10 | name: name, 11 | now: now, 12 | total: total, 13 | ifRemove: ifRemove, 14 | delayRemove: config.BarRemoveWaiteSec * time.Second, 15 | } 16 | } 17 | 18 | func NewRemoveBar(name string, now, total int) *Bar { 19 | return &Bar{ 20 | name: name, 21 | now: now, 22 | total: total, 23 | ifRemove: true, 24 | delayRemove: config.BarRemoveWaiteSec * time.Second, 25 | } 26 | } 27 | 28 | type Bar struct { 29 | name string 30 | now int 31 | total int 32 | completeTime time.Time 33 | delayRemove time.Duration 34 | ifRemove bool 35 | } 36 | 37 | func (b *Bar) shouldRemove() bool { 38 | if !b.ifRemove || b.now != b.total { 39 | return false 40 | } 41 | if b.completeTime.IsZero() { 42 | // first complete 43 | b.completeTime = time.Now() 44 | } 45 | if time.Since(b.completeTime) >= b.delayRemove { 46 | return true 47 | } 48 | return false 49 | } 50 | 51 | func (b *Bar) isDone() bool { 52 | return b.now == b.total 53 | } 54 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/sdk_user_simulator/user.go: -------------------------------------------------------------------------------- 1 | package sdk_user_simulator 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 8 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 9 | "github.com/openimsdk/tools/errs" 10 | ) 11 | 12 | var ( 13 | MapLock sync.Mutex 14 | UserMessageMap = make(map[string]*MsgListenerCallBak) 15 | timeOffset int64 16 | ) 17 | 18 | func GetRelativeServerTime() int64 { 19 | return utils.GetCurrentTimestampByMill() + timeOffset 20 | } 21 | 22 | func InitSDK(userID string, cf sdk_struct.IMConfig) (*open_im_sdk.UserContext, error) { 23 | userForSDK := open_im_sdk.NewLoginMgr() 24 | var testConnListener testConnListener 25 | testConnListener.UserID = userID 26 | isInit := userForSDK.InitSDK(&cf, &testConnListener) 27 | if !isInit { 28 | return nil, errs.New("sdk init failed").Wrap() 29 | } 30 | userForSDK.InitResources() 31 | 32 | SetListener(userForSDK, userID) 33 | 34 | return userForSDK, nil 35 | } 36 | 37 | func SetListener(userForSDK *open_im_sdk.UserContext, userID string) { 38 | var testConversation conversationCallBack 39 | userForSDK.SetConversationListener(&testConversation) 40 | var testUser userCallback 41 | userForSDK.SetUserListener(testUser) 42 | 43 | msgCallBack := NewMsgListenerCallBak(userID) 44 | MapLock.Lock() 45 | UserMessageMap[userID] = msgCallBack 46 | MapLock.Unlock() 47 | userForSDK.SetAdvancedMsgListener(msgCallBack) 48 | 49 | var friendshipListener testFriendshipListener 50 | userForSDK.SetFriendshipListener(friendshipListener) 51 | 52 | var groupListener testGroupListener 53 | userForSDK.SetGroupListener(groupListener) 54 | } 55 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/utils/context.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "context" 4 | 5 | func CancelAndReBuildCtx( 6 | buildFunc func(ctx context.Context) context.Context, 7 | cancel ...context.CancelFunc, 8 | ) context.Context { 9 | for _, cf := range cancel { 10 | cf() 11 | } 12 | return buildFunc(nil) 13 | } 14 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/utils/err_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/openimsdk/tools/errs" 6 | "testing" 7 | ) 8 | 9 | func TestErr(t *testing.T) { 10 | err := A5() 11 | fmt.Println(FormatErrorStack(err)) 12 | } 13 | 14 | func A1() error { 15 | err := errs.New("err1").Wrap() 16 | return err 17 | } 18 | 19 | func A2() error { 20 | return A1() 21 | } 22 | 23 | func A3() error { 24 | return A2() 25 | } 26 | 27 | func A4() error { 28 | return A3() 29 | } 30 | 31 | func A5() error { 32 | return A4() 33 | } 34 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/utils/error.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func FormatErrorStack(err error) string { 9 | if err == nil { 10 | return "" 11 | } 12 | 13 | var sb strings.Builder 14 | sb.WriteString(fmt.Sprintf("%+v", err)) 15 | 16 | stack := sb.String() 17 | stack = strings.ReplaceAll(stack, "\n", " => ") 18 | stack = strings.ReplaceAll(stack, "\t", " ") 19 | 20 | return stack 21 | } 22 | -------------------------------------------------------------------------------- /integration_test/internal/pkg/utils/group.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 4 | 5 | func BuildGroupName(ownerID, type_ string) string { 6 | return vars.GroupNamePrefix + type_ + "_" + ownerID 7 | } 8 | -------------------------------------------------------------------------------- /integration_test/internal/process/process_helper.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | func AddConditions(p *Process, condition ...bool) { 4 | p.RunConditions = append(p.RunConditions, condition...) 5 | } 6 | -------------------------------------------------------------------------------- /integration_test/internal/process/task.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | type Task struct { 4 | ShouldRun bool // determine if task will run 5 | Func any // must be func. run funcs 6 | Args []any // func args 7 | NegativeFunc any // must be func. if !ShouldRun, will run this 8 | NegativeArgs []any // negative args 9 | } 10 | 11 | func NewTask(shouldRun bool, f any, args ...any) *Task { 12 | return &Task{ 13 | ShouldRun: shouldRun, 14 | Func: f, 15 | Args: args, 16 | } 17 | } 18 | 19 | func (t *Task) AddNegativeFunc(f any, args ...any) *Task { 20 | t.NegativeFunc = f 21 | t.NegativeArgs = args 22 | return t 23 | } 24 | -------------------------------------------------------------------------------- /integration_test/internal/sdk/conversation.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (s *TestSDK) GetTotalConversationCount(ctx context.Context) (int, error) { 8 | res, err := s.SDK.Conversation().GetAllConversationList(ctx) 9 | if err != nil { 10 | return 0, err 11 | } 12 | return len(res), nil 13 | } 14 | -------------------------------------------------------------------------------- /integration_test/internal/sdk/msg.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 6 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 7 | ) 8 | 9 | func (s *TestSDK) SendSingleMsg(ctx context.Context, msg *sdk_struct.MsgStruct, receiveID string) (*sdk_struct.MsgStruct, error) { 10 | vars.SendMsgCount.Add(1) 11 | res, err := s.SDK.Conversation().SendMessage(ctx, msg, receiveID, "", nil, false) 12 | if err != nil { 13 | return nil, err 14 | } 15 | return res, nil 16 | } 17 | 18 | func (s *TestSDK) SendGroupMsg(ctx context.Context, msg *sdk_struct.MsgStruct, groupID string) (*sdk_struct.MsgStruct, error) { 19 | vars.SendMsgCount.Add(1) 20 | res, err := s.SDK.Conversation().SendMessage(ctx, msg, "", groupID, nil, false) 21 | if err != nil { 22 | return nil, err 23 | } 24 | return res, nil 25 | } 26 | -------------------------------------------------------------------------------- /integration_test/internal/sdk/relation.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 7 | ) 8 | 9 | func (s *TestSDK) GetAllFriends(ctx context.Context) ([]*model_struct.LocalFriend, error) { 10 | res, err := s.SDK.Relation().GetFriendList(ctx, false) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | return res, nil 16 | 17 | } 18 | -------------------------------------------------------------------------------- /integration_test/internal/sdk/sdk.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 5 | ) 6 | 7 | var ( 8 | // TestSDKs SDK slice. Index is user num 9 | TestSDKs []*TestSDK 10 | ) 11 | 12 | type TestSDK struct { 13 | UserID string 14 | Num int 15 | SDK *open_im_sdk.UserContext 16 | } 17 | 18 | func NewTestSDK(userID string, num int, loginMgr *open_im_sdk.UserContext) *TestSDK { 19 | return &TestSDK{ 20 | UserID: userID, 21 | Num: num, 22 | SDK: loginMgr, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /integration_test/internal/statistics/msg.go: -------------------------------------------------------------------------------- 1 | package statistics 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config" 7 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator" 8 | "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars" 9 | "github.com/openimsdk/tools/log" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func MsgConsuming(ctx context.Context) { 15 | defer decorator.FuncLog(ctx)() 16 | time.Sleep(time.Second * 5) 17 | close(vars.RecvMsgConsuming) 18 | var ( 19 | low int 20 | mid int 21 | high int 22 | outHigh int 23 | minT *vars.StatMsg 24 | maxT *vars.StatMsg 25 | totalCost int64 26 | count int 27 | ) 28 | for msg := range vars.RecvMsgConsuming { 29 | 30 | sec := msg.CostTime 31 | switch { 32 | case sec < config.ReceiveMsgTimeThresholdLow*1000: 33 | low++ 34 | case sec < config.ReceiveMsgTimeThresholdMedium*1000: 35 | mid++ 36 | case sec < config.ReceiveMsgTimeThresholdHigh*1000: 37 | high++ 38 | default: 39 | outHigh++ 40 | } 41 | 42 | if minT == nil || minT.CostTime > sec { 43 | minT = msg 44 | } 45 | if maxT == nil || maxT.CostTime < sec { 46 | maxT = msg 47 | } 48 | 49 | totalCost += sec 50 | count++ 51 | } 52 | 53 | if minT == nil || maxT == nil { 54 | return 55 | } 56 | statStr := ` 57 | statistic msg count: %d 58 | statistic send msg count: %d 59 | receive msg in %d s count: %d 60 | receive msg in %d s count: %d 61 | receive msg in %d s count: %d 62 | receive messages within %d s or more: %d 63 | maximum time to receive messages: %d ms, create: %d, receive: %d, msg: %v 64 | minimum time to receive messages: %d ms, create: %d, receive: %d, msg: %v 65 | average time consuming: %.2f ms 66 | ` 67 | statStr = fmt.Sprintf(statStr, 68 | count, 69 | vars.SendMsgCount.Load(), 70 | config.ReceiveMsgTimeThresholdLow, low, 71 | config.ReceiveMsgTimeThresholdMedium, mid, 72 | config.ReceiveMsgTimeThresholdHigh, high, 73 | config.ReceiveMsgTimeThresholdHigh, outHigh, 74 | maxT.CostTime, maxT.Msg.CreateTime, maxT.ReceiveTime, *maxT.Msg, 75 | minT.CostTime, minT.Msg.CreateTime, minT.ReceiveTime, *minT.Msg, 76 | float64(totalCost)/float64(count)) 77 | 78 | fmt.Println(statStr) 79 | log.ZInfo(ctx, "stat msg consuming", "res", strings.Replace(statStr, "\n", "; ", -1)) 80 | } 81 | -------------------------------------------------------------------------------- /integration_test/internal/vars/cmd.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | var ( 4 | UserNum int // user num 5 | SuperUserNum int // number of users with all friends 6 | LargeGroupNum int // number of large group 7 | LargeGroupMemberNum int // number of large group member num 8 | CommonGroupNum int // number of common group create by each user 9 | CommonGroupMemberNum int // number of common group member num 10 | SingleMessageNum int // number of single message each user send 11 | GroupMessageNum int // number of group message each user send 12 | 13 | ShouldRegister bool // determine whether register 14 | ShouldImportFriends bool // determine whether import friends 15 | ShouldCreateGroup bool // determine whether create group 16 | ShouldSendMsg bool // determine whether send messages 17 | 18 | ShouldCheckGroupNum bool // determine whether check group num 19 | ShouldCheckConversationNum bool // determine whether check conversation num 20 | ShouldCheckMessageNum bool // determine whether check message num 21 | ShouldCheckUninsAndReins bool // determine whether check again after uninstall and reinstall 22 | 23 | LoginRate float64 // number of login user rate 24 | ) 25 | 26 | var ( 27 | FlagMap = map[string]string{ 28 | "TestMode": "test", 29 | "UserNum": "u", 30 | "SuperUserNum": "su", 31 | "LargeGroupNum": "lg", 32 | "LargeGroupMemberNum": "lgm", 33 | "CommonGroupNum": "cg", 34 | "CommonGroupMemberNum": "cgm", 35 | "SingleMessageNum": "sm", 36 | "GroupMessageNum": "gm", 37 | "ShouldRegister": "reg", 38 | "ShouldImportFriends": "imf", 39 | "ShouldCreateGroup": "crg", 40 | "ShouldSendMsg": "sem", 41 | "ShouldCheckGroupNum": "ckgn", 42 | "ShouldCheckConversationNum": "ckcon", 43 | "ShouldCheckMessageNum": "ckmsn", 44 | "ShouldCheckUninsAndReins": "ckuni", 45 | "LoginRate": "lgr", 46 | } 47 | ) 48 | 49 | var ( 50 | IsLogin = false 51 | ) 52 | -------------------------------------------------------------------------------- /integration_test/internal/vars/group.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | const ( 4 | GroupNamePrefix = "group_test_" 5 | ) 6 | 7 | const ( 8 | CommonGroup = "common" 9 | LargeGroup = "large" 10 | ) 11 | -------------------------------------------------------------------------------- /integration_test/internal/vars/msg.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 5 | "sync/atomic" 6 | ) 7 | 8 | type StatMsg struct { 9 | CostTime int64 10 | ReceiveTime int64 11 | Msg *sdk_struct.MsgStruct 12 | } 13 | 14 | var ( 15 | SendMsgCount atomic.Int64 16 | RecvMsgConsuming chan *StatMsg 17 | ) 18 | -------------------------------------------------------------------------------- /integration_test/internal/vars/os.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import "os" 4 | 5 | var ( 6 | OsStdout = os.Stdout 7 | ) 8 | -------------------------------------------------------------------------------- /integration_test/internal/vars/user.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "context" 5 | "sync/atomic" 6 | ) 7 | 8 | const ( 9 | UserIDPrefix = "test_v3_u" 10 | ) 11 | 12 | var ( 13 | UserIDs []string // all user ids 14 | SuperUserIDs []string // user ids of users with all friends 15 | 16 | Contexts []context.Context // users contexts 17 | Cancels []context.CancelFunc // users contexts 18 | 19 | LoginUserNum int 20 | NowLoginNum atomic.Int64 21 | ) 22 | -------------------------------------------------------------------------------- /internal/conversation_msg/image.go: -------------------------------------------------------------------------------- 1 | package conversation_msg 2 | 3 | import ( 4 | "image" 5 | _ "image/gif" 6 | _ "image/jpeg" 7 | _ "image/png" 8 | "os" 9 | 10 | _ "golang.org/x/image/bmp" 11 | _ "golang.org/x/image/tiff" 12 | _ "golang.org/x/image/webp" 13 | 14 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 15 | "github.com/openimsdk/tools/errs" 16 | ) 17 | 18 | func getImageInfo(filePath string) (*sdk_struct.ImageInfo, error) { 19 | file, err := os.Open(filePath) 20 | if err != nil { 21 | return nil, errs.WrapMsg(err, "image file open err") 22 | } 23 | defer file.Close() 24 | info, err := file.Stat() 25 | if err != nil { 26 | return nil, err 27 | } 28 | img, format, err := image.Decode(file) 29 | if err != nil { 30 | return nil, errs.WrapMsg(err, "image file decode err") 31 | } 32 | size := img.Bounds().Max 33 | return &sdk_struct.ImageInfo{Width: int32(size.X), Height: int32(size.Y), Type: "image/" + format, Size: info.Size()}, nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/conversation_msg/max_seq_recorder.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package conversation_msg 16 | 17 | import "sync" 18 | 19 | type MaxSeqRecorder struct { 20 | seqs map[string]int64 21 | lock sync.RWMutex 22 | } 23 | 24 | func NewMaxSeqRecorder() MaxSeqRecorder { 25 | m := make(map[string]int64) 26 | return MaxSeqRecorder{seqs: m} 27 | } 28 | 29 | func (m *MaxSeqRecorder) Get(conversationID string) int64 { 30 | m.lock.RLock() 31 | defer m.lock.RUnlock() 32 | return m.seqs[conversationID] 33 | } 34 | 35 | func (m *MaxSeqRecorder) Set(conversationID string, seq int64) { 36 | m.lock.Lock() 37 | defer m.lock.Unlock() 38 | m.seqs[conversationID] = seq 39 | } 40 | 41 | func (m *MaxSeqRecorder) Incr(conversationID string, num int64) { 42 | m.lock.Lock() 43 | defer m.lock.Unlock() 44 | m.seqs[conversationID] += num 45 | } 46 | 47 | func (m *MaxSeqRecorder) IsNewMsg(conversationID string, seq int64) bool { 48 | m.lock.RLock() 49 | defer m.lock.RUnlock() 50 | currentSeq := m.seqs[conversationID] 51 | return seq > currentSeq 52 | } 53 | -------------------------------------------------------------------------------- /internal/flagconst/flag.go: -------------------------------------------------------------------------------- 1 | package flagconst 2 | 3 | var ( 4 | TestMode = false // if it`s true, it is test mode. 5 | ) 6 | -------------------------------------------------------------------------------- /internal/group/cache.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 7 | "github.com/openimsdk/tools/log" 8 | "github.com/openimsdk/tools/utils/datautil" 9 | ) 10 | 11 | func (g *Group) buildGroupMemberKey(groupID string, userID string) string { 12 | return groupID + ":" + userID 13 | } 14 | 15 | func (g *Group) GetGroupMembersInfoFunc(ctx context.Context, groupID string, userIDs []string, 16 | fetchFunc func(ctx context.Context, missingKeys []string) ([]*model_struct.LocalGroupMember, error), 17 | ) (map[string]*model_struct.LocalGroupMember, error) { 18 | var ( 19 | res = make(map[string]*model_struct.LocalGroupMember) 20 | missingKeys []string 21 | ) 22 | 23 | for _, userID := range userIDs { 24 | key := g.buildGroupMemberKey(groupID, userID) 25 | if member, ok := g.groupMemberCache.Load(key); ok { 26 | res[userID] = member 27 | } else { 28 | missingKeys = append(missingKeys, userIDs...) 29 | } 30 | } 31 | 32 | log.ZDebug(ctx, "GetGroupMembersInfoFunc fetch", "missingKeys", missingKeys) 33 | fetchData, err := fetchFunc(ctx, missingKeys) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | for i, data := range fetchData { 39 | key := g.buildGroupMemberKey(groupID, data.UserID) 40 | res[data.UserID] = fetchData[i] 41 | g.groupMemberCache.Store(key, fetchData[i]) 42 | } 43 | 44 | return res, nil 45 | } 46 | 47 | func (g *Group) GetGroupMembersInfo(ctx context.Context, groupID string, userIDs []string) (map[string]*model_struct.LocalGroupMember, error) { 48 | return g.GetGroupMembersInfoFunc(ctx, groupID, userIDs, func(ctx context.Context, dbKeys []string) ([]*model_struct.LocalGroupMember, error) { 49 | if len(dbKeys) == 0 { 50 | return nil, nil 51 | } 52 | dbData, err := g.db.GetGroupSomeMemberInfo(ctx, groupID, dbKeys) 53 | if err != nil { 54 | return nil, err 55 | } 56 | queryKeys := datautil.SliceSubAny(dbKeys, dbData, func(t *model_struct.LocalGroupMember) string { 57 | return t.UserID 58 | }) 59 | if len(queryKeys) != 0 { 60 | queryData, err := g.GetDesignatedGroupMembers(ctx, groupID, queryKeys) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | dbData = append(dbData, datautil.Batch(ServerGroupMemberToLocalGroupMember, queryData)...) 66 | } 67 | return dbData, nil 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /internal/interaction/constant.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package interaction 16 | 17 | const ( 18 | WebSocket = iota 19 | Tcp 20 | ) 21 | 22 | const ( 23 | // MessageText is for UTF-8 encoded text messages like JSON. 24 | MessageText = iota + 1 25 | // MessageBinary is for binary messages like protobufs. 26 | MessageBinary 27 | // CloseMessage denotes a close control message. The optional message 28 | // payload contains a numeric code and text. Use the FormatCloseMessage 29 | // function to format a close message payload. 30 | CloseMessage = 8 31 | 32 | // PingMessage denotes a ping control message. The optional message payload 33 | // is UTF-8 encoded text. 34 | PingMessage = 9 35 | 36 | // PongMessage denotes a pong control message. The optional message payload 37 | // is UTF-8 encoded text. 38 | PongMessage = 10 39 | ) 40 | -------------------------------------------------------------------------------- /internal/interaction/context.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package interaction 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/openimsdk/protocol/constant" 21 | ) 22 | 23 | type ConnContext struct { 24 | RemoteAddr string 25 | } 26 | 27 | func (c *ConnContext) Deadline() (deadline time.Time, ok bool) { 28 | return 29 | } 30 | 31 | func (c *ConnContext) Done() <-chan struct{} { 32 | return nil 33 | } 34 | 35 | func (c *ConnContext) Err() error { 36 | return nil 37 | } 38 | 39 | func (c *ConnContext) Value(key any) any { 40 | switch key { 41 | case constant.RemoteAddr: 42 | return c.RemoteAddr 43 | default: 44 | return "" 45 | } 46 | } 47 | 48 | func newContext(remoteAddr string) *ConnContext { 49 | return &ConnContext{ 50 | RemoteAddr: remoteAddr, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/interaction/encoder.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package interaction 16 | 17 | import ( 18 | "bytes" 19 | "encoding/gob" 20 | "github.com/openimsdk/tools/errs" 21 | ) 22 | 23 | type Encoder interface { 24 | Encode(data interface{}) ([]byte, error) 25 | Decode(encodeData []byte, decodeData interface{}) error 26 | } 27 | 28 | type GobEncoder struct { 29 | } 30 | 31 | func NewGobEncoder() *GobEncoder { 32 | return &GobEncoder{} 33 | } 34 | func (g *GobEncoder) Encode(data interface{}) ([]byte, error) { 35 | buff := bytes.Buffer{} 36 | enc := gob.NewEncoder(&buff) 37 | err := enc.Encode(data) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return buff.Bytes(), nil 42 | } 43 | func (g *GobEncoder) Decode(encodeData []byte, decodeData interface{}) error { 44 | buff := bytes.NewBuffer(encodeData) 45 | dec := gob.NewDecoder(buff) 46 | err := dec.Decode(decodeData) 47 | if err != nil { 48 | return errs.Wrap(err) 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/interaction/long_connection.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package interaction 16 | 17 | import ( 18 | "net/http" 19 | "time" 20 | ) 21 | 22 | type PingPongHandler func(string) error 23 | 24 | type LongConn interface { 25 | // Close closes this connection. 26 | Close() error 27 | 28 | // WriteMessage writes a message to the connection. 29 | // messageType indicates the type of data and can be set to binary (2) or text (1). 30 | WriteMessage(messageType int, message []byte) error 31 | 32 | // ReadMessage reads a message from the connection. 33 | ReadMessage() (int, []byte, error) 34 | 35 | // SetReadDeadline sets the deadline for reading from the underlying network connection. 36 | // After a timeout, there will be an error in the writing process. 37 | SetReadDeadline(timeout time.Duration) error 38 | 39 | // SetWriteDeadline sets the deadline for writing to the connection. 40 | // After a timeout, there will be an error in the writing process. 41 | SetWriteDeadline(timeout time.Duration) error 42 | 43 | // Dial tries to establish a connection. 44 | // urlStr must include authentication arguments; requestHeader can control data compression. 45 | Dial(urlStr string, requestHeader http.Header) (*http.Response, error) 46 | 47 | // IsNil checks whether the current long connection is nil. 48 | IsNil() bool 49 | 50 | // SetReadLimit sets the maximum size for a message read from the peer in bytes. 51 | SetReadLimit(limit int64) 52 | 53 | // SetPingHandler sets the handler for ping messages. 54 | SetPingHandler(handler PingPongHandler) 55 | 56 | // SetPongHandler sets the handler for pong messages. 57 | SetPongHandler(handler PingPongHandler) 58 | 59 | // LocalAddr returns the local network address. 60 | LocalAddr() string 61 | } 62 | -------------------------------------------------------------------------------- /internal/interaction/online.go: -------------------------------------------------------------------------------- 1 | package interaction 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/pkg/constant" 6 | userPb "github.com/openimsdk/protocol/user" 7 | ) 8 | 9 | func (c *LongConnMgr) subscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) { 10 | if len(userIDs) == 0 { 11 | return []*userPb.OnlineStatus{}, nil 12 | } 13 | res, err := c.GetUserOnlinePlatformIDs(ctx, userIDs) 14 | if err != nil { 15 | return nil, err 16 | } 17 | status := make([]*userPb.OnlineStatus, 0, len(res)) 18 | for userID, platformIDs := range res { 19 | value := &userPb.OnlineStatus{ 20 | UserID: userID, 21 | PlatformIDs: platformIDs, 22 | } 23 | if len(platformIDs) == 0 { 24 | value.Status = constant.Offline 25 | } else { 26 | value.Status = constant.Online 27 | } 28 | status = append(status, value) 29 | } 30 | return status, nil 31 | } 32 | 33 | func (c *LongConnMgr) UnsubscribeUsersStatus(ctx context.Context, userIDs []string) error { 34 | return c.UnsubscribeUserOnlinePlatformIDs(ctx, userIDs) 35 | } 36 | 37 | func (c *LongConnMgr) SubscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) { 38 | if len(userIDs) == 0 { 39 | return []*userPb.OnlineStatus{}, nil 40 | } 41 | return c.subscribeUsersStatus(ctx, userIDs) 42 | } 43 | 44 | func (c *LongConnMgr) GetSubscribeUsersStatus(ctx context.Context) ([]*userPb.OnlineStatus, error) { 45 | return c.subscribeUsersStatus(ctx, nil) 46 | } 47 | -------------------------------------------------------------------------------- /internal/interaction/reconnect.go: -------------------------------------------------------------------------------- 1 | package interaction 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type ReconnectStrategy interface { 8 | GetSleepInterval() time.Duration 9 | Reset() 10 | } 11 | 12 | type ExponentialRetry struct { 13 | attempts []int 14 | index int 15 | } 16 | 17 | func NewExponentialRetry() *ExponentialRetry { 18 | return &ExponentialRetry{ 19 | attempts: []int{1, 2, 4, 8, 16}, 20 | index: -1, 21 | } 22 | } 23 | 24 | func (rs *ExponentialRetry) GetSleepInterval() time.Duration { 25 | rs.index++ 26 | interval := rs.index % len(rs.attempts) 27 | return time.Second * time.Duration(rs.attempts[interval]) 28 | } 29 | 30 | func (rs *ExponentialRetry) Reset() { 31 | rs.index = -1 32 | } 33 | -------------------------------------------------------------------------------- /internal/interaction/subscription_test.go: -------------------------------------------------------------------------------- 1 | package interaction 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func TestName(t *testing.T) { 9 | sub := newSubscription() 10 | 11 | //sub.setUserState([]*sdkws.SubUserOnlineStatusElem{ 12 | // { 13 | // UserID: "1", 14 | // OnlinePlatformIDs: []int32{1, 2, 3}, 15 | // }, 16 | // { 17 | // UserID: "2", 18 | // OnlinePlatformIDs: []int32{1}, 19 | // }, 20 | //}) 21 | 22 | exist, wait, subUserIDs, unsubUserIDs := sub.getUserOnline([]string{"1", "2", "3"}) 23 | 24 | t.Logf("exist: %v", exist) 25 | t.Logf("wait: %v", wait) 26 | t.Logf("subUserIDs: %v", subUserIDs) 27 | t.Logf("unsubUserIDs: %v", unsubUserIDs) 28 | 29 | sub.writeFailed(wait, errors.New("todo test")) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /internal/interaction/ws_default.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | 17 | package interaction 18 | 19 | import ( 20 | "net/http" 21 | "time" 22 | 23 | "github.com/gorilla/websocket" 24 | ) 25 | 26 | type Default struct { 27 | ConnType int 28 | conn *websocket.Conn 29 | isSetConf bool 30 | } 31 | 32 | func (d *Default) SetReadDeadline(timeout time.Duration) error { 33 | return d.conn.SetReadDeadline(time.Now().Add(timeout)) 34 | } 35 | 36 | func (d *Default) SetWriteDeadline(timeout time.Duration) error { 37 | return d.conn.SetWriteDeadline(time.Now().Add(timeout)) 38 | } 39 | 40 | func (d *Default) SetReadLimit(limit int64) { 41 | d.conn.SetReadLimit(limit) 42 | 43 | } 44 | 45 | func (d *Default) SetPingHandler(handler PingPongHandler) { 46 | d.conn.SetPingHandler(handler) 47 | } 48 | 49 | func (d *Default) SetPongHandler(handler PingPongHandler) { 50 | d.conn.SetPongHandler(handler) 51 | } 52 | 53 | func (d *Default) LocalAddr() string { 54 | return d.conn.LocalAddr().String() 55 | } 56 | 57 | func NewWebSocket(connType int) *Default { 58 | return &Default{ConnType: connType} 59 | } 60 | func (d *Default) Close() error { 61 | return d.conn.Close() 62 | } 63 | 64 | func (d *Default) WriteMessage(messageType int, message []byte) error { 65 | return d.conn.WriteMessage(messageType, message) 66 | } 67 | 68 | func (d *Default) ReadMessage() (int, []byte, error) { 69 | return d.conn.ReadMessage() 70 | } 71 | 72 | func (d *Default) Dial(urlStr string, requestHeader http.Header) (*http.Response, error) { 73 | conn, httpResp, err := websocket.DefaultDialer.Dial(urlStr, requestHeader) 74 | if err == nil { 75 | d.conn = conn 76 | } 77 | return httpResp, err 78 | 79 | } 80 | 81 | func (d *Default) IsNil() bool { 82 | if d.conn != nil { 83 | return false 84 | } 85 | return true 86 | } 87 | -------------------------------------------------------------------------------- /internal/relation/conversion.go: -------------------------------------------------------------------------------- 1 | package relation 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 5 | "github.com/openimsdk/protocol/sdkws" 6 | ) 7 | 8 | func ServerFriendRequestToLocalFriendRequest(info *sdkws.FriendRequest) *model_struct.LocalFriendRequest { 9 | return &model_struct.LocalFriendRequest{ 10 | FromUserID: info.FromUserID, 11 | FromNickname: info.FromNickname, 12 | FromFaceURL: info.FromFaceURL, 13 | //FromGender: info.FromGender, 14 | ToUserID: info.ToUserID, 15 | ToNickname: info.ToNickname, 16 | ToFaceURL: info.ToFaceURL, 17 | //ToGender: info.ToGender, 18 | HandleResult: info.HandleResult, 19 | ReqMsg: info.ReqMsg, 20 | CreateTime: info.CreateTime, 21 | HandlerUserID: info.HandlerUserID, 22 | HandleMsg: info.HandleMsg, 23 | HandleTime: info.HandleTime, 24 | Ex: info.Ex, 25 | //AttachedInfo: info.AttachedInfo, 26 | } 27 | } 28 | 29 | func ServerFriendToLocalFriend(info *sdkws.FriendInfo) *model_struct.LocalFriend { 30 | return &model_struct.LocalFriend{ 31 | OwnerUserID: info.OwnerUserID, 32 | FriendUserID: info.FriendUser.UserID, 33 | Remark: info.Remark, 34 | CreateTime: info.CreateTime, 35 | AddSource: info.AddSource, 36 | OperatorUserID: info.OperatorUserID, 37 | Nickname: info.FriendUser.Nickname, 38 | FaceURL: info.FriendUser.FaceURL, 39 | Ex: info.Ex, 40 | //AttachedInfo: info.FriendUser.AttachedInfo, 41 | IsPinned: info.IsPinned, 42 | } 43 | } 44 | 45 | func ServerBlackToLocalBlack(info *sdkws.BlackInfo) *model_struct.LocalBlack { 46 | return &model_struct.LocalBlack{ 47 | OwnerUserID: info.OwnerUserID, 48 | BlockUserID: info.BlackUserInfo.UserID, 49 | CreateTime: info.CreateTime, 50 | AddSource: info.AddSource, 51 | OperatorUserID: info.OperatorUserID, 52 | Nickname: info.BlackUserInfo.Nickname, 53 | FaceURL: info.BlackUserInfo.FaceURL, 54 | Ex: info.Ex, 55 | //AttachedInfo: info.FriendUser.AttachedInfo, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/relation/incremental_sync.go: -------------------------------------------------------------------------------- 1 | package relation 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer" 8 | "github.com/openimsdk/protocol/relation" 9 | "github.com/openimsdk/tools/utils/datautil" 10 | ) 11 | 12 | func (r *Relation) IncrSyncFriends(ctx context.Context) error { 13 | friendSyncer := syncer.VersionSynchronizer[*model_struct.LocalFriend, *relation.GetIncrementalFriendsResp]{ 14 | Ctx: ctx, 15 | DB: r.db, 16 | TableName: r.friendListTableName(), 17 | EntityID: r.loginUserID, 18 | Key: func(localFriend *model_struct.LocalFriend) string { 19 | return localFriend.FriendUserID 20 | }, 21 | Local: func() ([]*model_struct.LocalFriend, error) { 22 | return r.db.GetAllFriendList(ctx) 23 | }, 24 | Server: func(version *model_struct.LocalVersionSync) (*relation.GetIncrementalFriendsResp, error) { 25 | return r.getIncrementalFriends(ctx, &relation.GetIncrementalFriendsReq{ 26 | UserID: r.loginUserID, 27 | Version: version.Version, 28 | VersionID: version.VersionID, 29 | }) 30 | }, 31 | Full: func(resp *relation.GetIncrementalFriendsResp) bool { 32 | return resp.Full 33 | }, 34 | Version: func(resp *relation.GetIncrementalFriendsResp) (string, uint64) { 35 | return resp.VersionID, resp.Version 36 | }, 37 | Delete: func(resp *relation.GetIncrementalFriendsResp) []string { 38 | return resp.Delete 39 | }, 40 | Update: func(resp *relation.GetIncrementalFriendsResp) []*model_struct.LocalFriend { 41 | return datautil.Batch(ServerFriendToLocalFriend, resp.Update) 42 | }, 43 | Insert: func(resp *relation.GetIncrementalFriendsResp) []*model_struct.LocalFriend { 44 | return datautil.Batch(ServerFriendToLocalFriend, resp.Insert) 45 | }, 46 | Syncer: func(server, local []*model_struct.LocalFriend) error { 47 | return r.friendSyncer.Sync(ctx, server, local, nil) 48 | }, 49 | FullSyncer: func(ctx context.Context) error { 50 | return r.friendSyncer.FullSync(ctx, r.loginUserID) 51 | }, 52 | FullID: func(ctx context.Context) ([]string, error) { 53 | resp, err := r.getFullFriendUserIDs(ctx, &relation.GetFullFriendUserIDsReq{ 54 | UserID: r.loginUserID, 55 | }) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return resp.UserIDs, nil 60 | }, 61 | IDOrderChanged: func(resp *relation.GetIncrementalFriendsResp) bool { 62 | return resp.SortVersion > 0 63 | }, 64 | } 65 | return friendSyncer.IncrementalSync() 66 | } 67 | 68 | func (r *Relation) IncrSyncFriendsWithLock(ctx context.Context) error { 69 | r.relationSyncMutex.Lock() 70 | defer r.relationSyncMutex.Unlock() 71 | return r.IncrSyncFriends(ctx) 72 | } 73 | 74 | func (r *Relation) friendListTableName() string { 75 | return model_struct.LocalFriend{}.TableName() 76 | } 77 | -------------------------------------------------------------------------------- /internal/third/api.go: -------------------------------------------------------------------------------- 1 | package third 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 7 | 8 | "github.com/openimsdk/protocol/third" 9 | ) 10 | 11 | func (c *Third) UpdateFcmToken(ctx context.Context, fcmToken string, expireTime int64) error { 12 | return api.FcmUpdateToken.Execute(ctx, &third.FcmUpdateTokenReq{ 13 | PlatformID: c.platform, 14 | FcmToken: fcmToken, 15 | Account: c.loginUserID, 16 | ExpireTime: expireTime, 17 | }) 18 | } 19 | 20 | func (c *Third) SetAppBadge(ctx context.Context, appUnreadCount int32) error { 21 | return api.SetAppBadge.Execute(ctx, &third.SetAppBadgeReq{ 22 | UserID: c.loginUserID, 23 | AppUnreadCount: appUnreadCount, 24 | }) 25 | } 26 | 27 | func (c *Third) UploadLogs(ctx context.Context, line int, ex string, progress Progress) (err error) { 28 | return c.uploadLogs(ctx, line, ex, progress) 29 | } 30 | 31 | func (c *Third) Log(ctx context.Context, logLevel int, file string, line int, msg, err string, keysAndValues []any) { 32 | c.printLog(ctx, logLevel, file, line, msg, err, keysAndValues) 33 | } 34 | -------------------------------------------------------------------------------- /internal/third/file/bitmap.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM open source community. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package file 16 | 17 | func NewBitmap(size int) *Bitmap { 18 | data := make([]uint64, (size+63)/64) 19 | return &Bitmap{data: data, size: size} 20 | } 21 | 22 | func ParseBitmap(p []byte, size int) *Bitmap { 23 | data := make([]uint64, len(p)/8) 24 | for i := range data { 25 | data[i] = uint64(p[i*8])<<56 | 26 | uint64(p[i*8+1])<<48 | 27 | uint64(p[i*8+2])<<40 | 28 | uint64(p[i*8+3])<<32 | 29 | uint64(p[i*8+4])<<24 | 30 | uint64(p[i*8+5])<<16 | 31 | uint64(p[i*8+6])<<8 | 32 | uint64(p[i*8+7]) 33 | } 34 | return &Bitmap{ 35 | data: data, 36 | size: size, 37 | } 38 | } 39 | 40 | type Bitmap struct { 41 | data []uint64 42 | size int 43 | } 44 | 45 | func (b *Bitmap) Set(index int) { 46 | if index < 0 || index >= b.size { 47 | panic("out of range") 48 | } 49 | wordIndex := index / 64 50 | bitIndex := uint(index % 64) 51 | b.data[wordIndex] |= 1 << bitIndex 52 | } 53 | 54 | func (b *Bitmap) Clear(index int) { 55 | if index < 0 || index >= b.size { 56 | panic("out of range") 57 | } 58 | wordIndex := index / 64 59 | bitIndex := uint(index % 64) 60 | b.data[wordIndex] &= ^(1 << bitIndex) 61 | } 62 | 63 | func (b *Bitmap) Get(index int) bool { 64 | if index < 0 || index >= b.size { 65 | panic("out of range") 66 | } 67 | wordIndex := index / 64 68 | bitIndex := uint(index % 64) 69 | return (b.data[wordIndex] & (1 << bitIndex)) != 0 70 | } 71 | 72 | func (b *Bitmap) Size() int { 73 | return b.size 74 | } 75 | 76 | func (b *Bitmap) Serialize() []byte { 77 | p := make([]byte, len(b.data)*8) 78 | for i, word := range b.data { 79 | p[i*8] = byte(word >> 56) 80 | p[i*8+1] = byte(word >> 48) 81 | p[i*8+2] = byte(word >> 40) 82 | p[i*8+3] = byte(word >> 32) 83 | p[i*8+4] = byte(word >> 24) 84 | p[i*8+5] = byte(word >> 16) 85 | p[i*8+6] = byte(word >> 8) 86 | p[i*8+7] = byte(word) 87 | } 88 | return p 89 | } 90 | -------------------------------------------------------------------------------- /internal/third/file/cb.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package file 16 | 17 | import "fmt" 18 | 19 | type UploadFileCallback interface { 20 | Open(size int64) // file opening size 21 | PartSize(partSize int64, num int) // shard size, number 22 | HashPartProgress(index int, size int64, partHash string) // hash value of each shard 23 | HashPartComplete(partsHash string, fileHash string) // sharding is complete, server marks hash and file final hash 24 | UploadID(uploadID string) // upload ID 25 | UploadPartComplete(index int, partSize int64, partHash string) // upload shard progress 26 | UploadComplete(fileSize int64, streamSize int64, storageSize int64) // overall progress 27 | Complete(size int64, url string, typ int) // upload completed 28 | } 29 | 30 | type emptyUploadCallback struct{} 31 | 32 | func (e emptyUploadCallback) Open(size int64) { 33 | fmt.Println("Callback Open:", size) 34 | } 35 | 36 | func (e emptyUploadCallback) PartSize(partSize int64, num int) { 37 | fmt.Println("Callback PartSize:", partSize, num) 38 | } 39 | 40 | func (e emptyUploadCallback) HashPartProgress(index int, size int64, partHash string) { 41 | //fmt.Println("Callback HashPartProgress:", index, size, partHash) 42 | } 43 | 44 | func (e emptyUploadCallback) HashPartComplete(partsHash string, fileHash string) { 45 | fmt.Println("Callback HashPartComplete:", partsHash, fileHash) 46 | } 47 | 48 | func (e emptyUploadCallback) UploadID(uploadID string) { 49 | fmt.Println("Callback UploadID:", uploadID) 50 | } 51 | 52 | func (e emptyUploadCallback) UploadPartComplete(index int, partSize int64, partHash string) { 53 | fmt.Println("Callback UploadPartComplete:", index, partSize, partHash) 54 | } 55 | 56 | func (e emptyUploadCallback) UploadComplete(fileSize int64, streamSize int64, storageSize int64) { 57 | fmt.Println("Callback UploadComplete:", fileSize, streamSize, storageSize) 58 | } 59 | 60 | func (e emptyUploadCallback) Complete(size int64, url string, typ int) { 61 | fmt.Println("Callback Complete:", size, url, typ) 62 | } 63 | -------------------------------------------------------------------------------- /internal/third/file/file.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM open source community. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package file 16 | 17 | import "io" 18 | 19 | type ReadFile interface { 20 | io.Reader 21 | io.Closer 22 | Size() int64 23 | StartSeek(whence int) error 24 | } 25 | -------------------------------------------------------------------------------- /internal/third/file/file_default.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM open source community. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | 17 | package file 18 | 19 | import ( 20 | "bufio" 21 | "io" 22 | "os" 23 | ) 24 | 25 | const readBufferSize = 1024 * 1024 * 5 // 5mb 26 | 27 | func Open(req *UploadFileReq) (ReadFile, error) { 28 | file, err := os.Open(req.Filepath) 29 | if err != nil { 30 | return nil, err 31 | } 32 | info, err := file.Stat() 33 | if err != nil { 34 | _ = file.Close() 35 | return nil, err 36 | } 37 | df := &defaultFile{ 38 | file: file, 39 | info: info, 40 | } 41 | df.resetReaderBuffer() 42 | return df, nil 43 | } 44 | 45 | type defaultFile struct { 46 | file *os.File 47 | info os.FileInfo 48 | reader io.Reader 49 | } 50 | 51 | func (d *defaultFile) resetReaderBuffer() { 52 | d.reader = bufio.NewReaderSize(d.file, readBufferSize) 53 | } 54 | 55 | func (d *defaultFile) Read(p []byte) (n int, err error) { 56 | return d.reader.Read(p) 57 | } 58 | 59 | func (d *defaultFile) Close() error { 60 | return d.file.Close() 61 | } 62 | 63 | func (d *defaultFile) StartSeek(whence int) error { 64 | if _, err := d.file.Seek(io.SeekStart, whence); err != nil { 65 | return err 66 | } 67 | d.resetReaderBuffer() 68 | return nil 69 | } 70 | 71 | func (d *defaultFile) Size() int64 { 72 | return d.info.Size() 73 | } 74 | -------------------------------------------------------------------------------- /internal/third/file/file_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext" 6 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 7 | "path/filepath" 8 | "testing" 9 | ) 10 | 11 | func TestUpload(t *testing.T) { 12 | conf := &ccontext.GlobalConfig{ 13 | UserID: `4931176757`, 14 | Token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI0OTMxMTc2NzU3IiwiUGxhdGZvcm1JRCI6MSwiZXhwIjoxNzA3MTE0MjIyLCJuYmYiOjE2OTkzMzc5MjIsImlhdCI6MTY5OTMzODIyMn0.AyNvrMGEdXD5rkvn7ZLHCNs-lNbDCb2otn97yLXia5Y`, 15 | IMConfig: sdk_struct.IMConfig{ 16 | ApiAddr: `http://203.56.175.233:10002`, 17 | }, 18 | } 19 | ctx := ccontext.WithInfo(context.WithValue(context.Background(), "operationID", "OP123456"), conf) 20 | f := NewFile(nil, conf.UserID) 21 | 22 | //fp := `C:\Users\openIM\Desktop\my_image (2).tar` 23 | //fp := `C:\Users\openIM\Desktop\1234.zip` 24 | //fp := `C:\Users\openIM\Desktop\openIM.wasm` 25 | //fp := `C:\Users\openIM\Desktop\ubuntu.7z` 26 | //fp := `C:\Users\openIM\Desktop\log2023-10-31.log` 27 | fp := `C:\Users\openIM\Desktop\protoc.zip` 28 | 29 | resp, err := f.UploadFile(ctx, &UploadFileReq{ 30 | Filepath: fp, 31 | Name: filepath.Base(fp), 32 | Cause: "test", 33 | }, nil) 34 | if err != nil { 35 | t.Fatal("failed", err) 36 | } 37 | t.Log("success", resp.URL) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /internal/third/file/md5.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package file 16 | 17 | import ( 18 | "crypto/md5" 19 | "encoding/hex" 20 | "hash" 21 | "io" 22 | ) 23 | 24 | func NewMd5Reader(r io.Reader) *Md5Reader { 25 | return &Md5Reader{h: md5.New(), r: r} 26 | } 27 | 28 | type Md5Reader struct { 29 | h hash.Hash 30 | r io.Reader 31 | } 32 | 33 | func (r *Md5Reader) Read(p []byte) (n int, err error) { 34 | n, err = r.r.Read(p) 35 | if err == nil && n > 0 { 36 | r.h.Write(p[:n]) 37 | } 38 | return 39 | } 40 | 41 | func (r *Md5Reader) Md5() string { 42 | return hex.EncodeToString(r.h.Sum(nil)) 43 | } 44 | -------------------------------------------------------------------------------- /internal/third/file/progress.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package file 16 | 17 | import ( 18 | "io" 19 | ) 20 | 21 | func NewProgressReader(r io.Reader, fn func(current int64)) io.Reader { 22 | if r == nil || fn == nil { 23 | return r 24 | } 25 | return &Reader{ 26 | r: r, 27 | fn: fn, 28 | } 29 | } 30 | 31 | type Reader struct { 32 | r io.Reader 33 | read int64 34 | fn func(current int64) 35 | } 36 | 37 | func (r *Reader) Read(p []byte) (n int, err error) { 38 | n, err = r.r.Read(p) 39 | if err == nil && n > 0 { 40 | r.read += int64(n) 41 | r.fn(r.read) 42 | } 43 | return n, err 44 | } 45 | -------------------------------------------------------------------------------- /internal/third/log_test.go: -------------------------------------------------------------------------------- 1 | package third 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | ) 9 | 10 | func TestLogMatch(t *testing.T) { 11 | 12 | filenames := []string{ 13 | "log1.txt", 14 | "log2.log", 15 | "log3.log.txt", 16 | "log4.log.2022-01-01", 17 | "log5.log.2022-01-01.txt", 18 | "log20230918.log", 19 | "OpenIM.CronTask.log.all.2023-09-18", "OpenIM.log.all.2023-09-18", 20 | } 21 | 22 | expected := []string{ 23 | "OpenIM.CronTask.log.all.2023-09-18", "OpenIM.log.all.2023-09-18", 24 | } 25 | 26 | var actual []string 27 | for _, filename := range filenames { 28 | if checkLogPath(filename) { 29 | actual = append(actual, filename) 30 | } 31 | } 32 | 33 | if len(actual) != len(expected) { 34 | t.Errorf("Expected %d matches, but got %d", len(expected), len(actual)) 35 | } 36 | 37 | for i := range expected { 38 | if actual[i] != expected[i] { 39 | t.Errorf("Expected match %d to be %q, but got %q", i, expected[i], actual[i]) 40 | } 41 | } 42 | } 43 | 44 | func TestName(t *testing.T) { 45 | dir := `C:\Users\openIM\Desktop\testlog` 46 | 47 | dirs, err := os.ReadDir(dir) 48 | if err != nil { 49 | panic(err) 50 | } 51 | for _, entry := range dirs { 52 | if !entry.IsDir() { 53 | info, err := entry.Info() 54 | if err != nil { 55 | panic(err) 56 | } 57 | fmt.Println(entry.Name(), info.Size(), info.ModTime()) 58 | } 59 | } 60 | 61 | if true { 62 | return 63 | } 64 | 65 | files := []string{ 66 | //filepath.Join(dir, "open-im-sdk-core.2023-10-13"), 67 | filepath.Join(dir, "open-im-sdk-core.2023-11-15"), 68 | //filepath.Join(dir, "open-im-sdk-core.2023-11-17"), 69 | } 70 | 71 | if err := zipFiles(filepath.Join(dir, "test1.zip"), files); err != nil { 72 | t.Error(err) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /internal/third/progress.go: -------------------------------------------------------------------------------- 1 | package third 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Progress interface { 8 | OnProgress(current int64, size int64) 9 | } 10 | 11 | type progressConvert struct { 12 | ctx context.Context 13 | p Progress 14 | } 15 | 16 | func (p *progressConvert) Open(size int64) { 17 | p.p.OnProgress(0, size) 18 | } 19 | 20 | func (p *progressConvert) PartSize(partSize int64, num int) {} 21 | 22 | func (p *progressConvert) HashPartProgress(index int, size int64, partHash string) {} 23 | 24 | func (p *progressConvert) HashPartComplete(partsHash string, fileHash string) {} 25 | 26 | func (p *progressConvert) UploadID(uploadID string) {} 27 | 28 | func (p *progressConvert) UploadPartComplete(index int, partSize int64, partHash string) {} 29 | 30 | func (p *progressConvert) UploadComplete(fileSize int64, streamSize int64, storageSize int64) { 31 | //log.ZDebug(p.ctx, "upload log progress", "fileSize", fileSize, "current", streamSize) 32 | p.p.OnProgress(streamSize, fileSize) 33 | } 34 | 35 | func (p *progressConvert) Complete(size int64, url string, typ int) { 36 | p.p.OnProgress(size, size) 37 | } 38 | -------------------------------------------------------------------------------- /internal/third/third.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package third 16 | 17 | import ( 18 | "sync" 19 | 20 | "github.com/openimsdk/openim-sdk-core/v3/internal/third/file" 21 | ) 22 | 23 | type Third struct { 24 | platform int32 25 | loginUserID string 26 | appFramework string 27 | LogFilePath string 28 | fileUploader *file.File 29 | logUploadLock sync.Mutex 30 | } 31 | 32 | func (t *Third) SetPlatform(platform int32) { 33 | t.platform = platform 34 | } 35 | 36 | func (t *Third) SetLoginUserID(loginUserID string) { 37 | t.loginUserID = loginUserID 38 | } 39 | 40 | func (t *Third) SetAppFramework(appFramework string) { 41 | t.appFramework = appFramework 42 | } 43 | 44 | func (t *Third) SetLogFilePath(LogFilePath string) { 45 | t.LogFilePath = LogFilePath 46 | } 47 | 48 | func NewThird(fileUploader *file.File) *Third { 49 | return &Third{fileUploader: fileUploader} 50 | } 51 | -------------------------------------------------------------------------------- /internal/third/zip.go: -------------------------------------------------------------------------------- 1 | package third 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | func (c *Third) addFileToZip(zipWriter *zip.Writer, filename string) error { 11 | file, err := os.Open(filename) 12 | if err != nil { 13 | return err 14 | } 15 | defer file.Close() 16 | info, err := file.Stat() 17 | if err != nil { 18 | return err 19 | } 20 | header, err := zip.FileInfoHeader(info) 21 | if err != nil { 22 | return err 23 | } 24 | header.Name = filepath.Base(filename) 25 | header.Method = zip.Deflate 26 | writer, err := zipWriter.CreateHeader(header) 27 | if err != nil { 28 | return err 29 | } 30 | _, err = io.Copy(writer, io.LimitReader(file, info.Size())) 31 | return err 32 | } 33 | 34 | func zipFiles(zipPath string, files []string) error { 35 | zipFile, err := os.Create(zipPath) 36 | if err != nil { 37 | return err 38 | } 39 | defer zipFile.Close() 40 | zipWriter := zip.NewWriter(zipFile) 41 | defer zipWriter.Close() 42 | addFileToZip := func(fp string) error { 43 | file, err := os.Open(fp) 44 | if err != nil { 45 | return err 46 | } 47 | defer file.Close() 48 | info, err := file.Stat() 49 | if err != nil { 50 | return err 51 | } 52 | header, err := zip.FileInfoHeader(info) 53 | if err != nil { 54 | return err 55 | } 56 | header.Name = filepath.Base(file.Name()) 57 | header.Method = zip.Deflate 58 | writer, err := zipWriter.CreateHeader(header) 59 | if err != nil { 60 | return err 61 | } 62 | _, err = io.Copy(writer, io.LimitReader(file, info.Size())) 63 | return err 64 | } 65 | for _, file := range files { 66 | err := addFileToZip(file) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | if err := zipWriter.Flush(); err != nil { 72 | return err 73 | } 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /internal/user/conversion.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 5 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 6 | "github.com/openimsdk/protocol/sdkws" 7 | ) 8 | 9 | func ServerUserToLocalUser(user *sdkws.UserInfo) *model_struct.LocalUser { 10 | return &model_struct.LocalUser{ 11 | UserID: user.UserID, 12 | Nickname: user.Nickname, 13 | FaceURL: user.FaceURL, 14 | CreateTime: user.CreateTime, 15 | Ex: user.Ex, 16 | //AppMangerLevel: user.AppMangerLevel, 17 | GlobalRecvMsgOpt: user.GlobalRecvMsgOpt, 18 | //AttachedInfo: user.AttachedInfo, 19 | } 20 | } 21 | func LocalUserToPublicUser(user *model_struct.LocalUser) *sdk_struct.PublicUser { 22 | return &sdk_struct.PublicUser{ 23 | UserID: user.UserID, 24 | Nickname: user.Nickname, 25 | FaceURL: user.FaceURL, 26 | Ex: user.Ex, 27 | CreateTime: user.CreateTime, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /internal/user/full_sync.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 8 | "github.com/openimsdk/tools/errs" 9 | "github.com/openimsdk/tools/log" 10 | ) 11 | 12 | func (u *User) SyncLoginUserInfo(ctx context.Context) error { 13 | remoteUser, err := u.GetSingleUserFromServer(ctx, u.loginUserID) 14 | if err != nil { 15 | return err 16 | } 17 | localUser, err := u.GetLoginUser(ctx, u.loginUserID) 18 | if err != nil && (!errs.ErrRecordNotFound.Is(errs.Unwrap(err))) { 19 | return err 20 | } 21 | var localUsers []*model_struct.LocalUser 22 | if err == nil { 23 | localUsers = []*model_struct.LocalUser{localUser} 24 | } 25 | log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser) 26 | return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil) 27 | } 28 | 29 | func (u *User) SyncLoginUserInfoWithoutNotice(ctx context.Context) error { 30 | remoteUser, err := u.GetSingleUserFromServer(ctx, u.loginUserID) 31 | if err != nil { 32 | return err 33 | } 34 | localUser, err := u.GetLoginUser(ctx, u.loginUserID) 35 | if err != nil && errors.Is(errs.Unwrap(err), errs.ErrRecordNotFound) { 36 | log.ZError(ctx, "SyncLoginUserInfo", err) 37 | } 38 | var localUsers []*model_struct.LocalUser 39 | if err == nil { 40 | localUsers = []*model_struct.LocalUser{localUser} 41 | } 42 | log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser) 43 | return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil, false, true) 44 | } 45 | -------------------------------------------------------------------------------- /internal/user/notification.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 7 | "github.com/openimsdk/protocol/constant" 8 | "github.com/openimsdk/protocol/sdkws" 9 | "github.com/openimsdk/tools/errs" 10 | "github.com/openimsdk/tools/log" 11 | ) 12 | 13 | // DoNotification handles incoming notifications for the user. 14 | func (u *User) DoNotification(ctx context.Context, msg *sdkws.MsgData) { 15 | log.ZDebug(ctx, "user notification", "msg", msg) 16 | if err := u.doNotification(ctx, msg); err != nil { 17 | log.ZError(ctx, "DoUserNotification failed", err) 18 | } 19 | } 20 | 21 | func (u *User) doNotification(ctx context.Context, msg *sdkws.MsgData) error { 22 | switch msg.ContentType { 23 | case constant.UserInfoUpdatedNotification: 24 | return u.userInfoUpdatedNotification(ctx, msg) 25 | default: 26 | return errs.New("unknown content type", "contentType", msg.ContentType, "clientMsgID", msg.ClientMsgID, "serverMsgID", msg.ServerMsgID).Wrap() 27 | } 28 | } 29 | 30 | // userInfoUpdatedNotification handles notifications about updated user information. 31 | func (u *User) userInfoUpdatedNotification(ctx context.Context, msg *sdkws.MsgData) error { 32 | tips := sdkws.UserInfoUpdatedTips{} 33 | if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil { 34 | return err 35 | } 36 | 37 | if tips.UserID == u.loginUserID { 38 | err := u.SyncLoginUserInfo(ctx) 39 | if err != nil { 40 | return err 41 | } 42 | } else { 43 | log.ZDebug(ctx, "detail.UserID != u.loginUserID, do nothing", "detail.UserID", tips.UserID, "u.loginUserID", u.loginUserID) 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/user/server_api.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 7 | "github.com/openimsdk/protocol/sdkws" 8 | "github.com/openimsdk/protocol/user" 9 | ) 10 | 11 | func (u *User) getUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) { 12 | req := &user.GetDesignateUsersReq{UserIDs: userIDs} 13 | return api.ExtractField(ctx, api.GetUsersInfo.Invoke, req, (*user.GetDesignateUsersResp).GetUsersInfo) 14 | } 15 | 16 | func (u *User) updateUserInfo(ctx context.Context, userInfo *sdkws.UserInfoWithEx) error { 17 | userInfo.UserID = u.loginUserID 18 | return api.UpdateUserInfoEx.Execute(ctx, &user.UpdateUserInfoExReq{UserInfo: userInfo}) 19 | } 20 | -------------------------------------------------------------------------------- /msgtest/config.go: -------------------------------------------------------------------------------- 1 | package msgtest 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 7 | "github.com/openimsdk/protocol/constant" 8 | ) 9 | 10 | // config here 11 | 12 | // system 13 | var ( 14 | TESTIP = "127.0.0.1" 15 | APIADDR = fmt.Sprintf("http://%v:10002", TESTIP) 16 | WSADDR = fmt.Sprintf("ws://%v:10001", TESTIP) 17 | SECRET = "openIM123" 18 | MANAGERUSERID = "openIMAdmin" 19 | 20 | PLATFORMID = constant.WindowsPlatformID 21 | LogLevel = uint32(5) 22 | ) 23 | 24 | func GetConfig() *sdk_struct.IMConfig { 25 | var cf sdk_struct.IMConfig 26 | cf.ApiAddr = APIADDR 27 | cf.PlatformID = int32(PLATFORMID) 28 | cf.WsAddr = WSADDR 29 | cf.DataDir = "./" 30 | cf.LogLevel = LogLevel 31 | cf.IsLogStandardOutput = true 32 | cf.LogFilePath = "" 33 | return &cf 34 | 35 | } 36 | -------------------------------------------------------------------------------- /msgtest/conversation_test.go: -------------------------------------------------------------------------------- 1 | package msgtest 2 | 3 | import "testing" 4 | 5 | func Test_CreateConversations(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /msgtest/message_test.go: -------------------------------------------------------------------------------- 1 | package msgtest 2 | 3 | import ( 4 | "context" 5 | "github.com/openimsdk/openim-sdk-core/v3/msgtest/module" 6 | "github.com/openimsdk/openim-sdk-core/v3/msgtest/sdk_user_simulator" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext" 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 9 | "github.com/openimsdk/tools/log" 10 | "testing" 11 | ) 12 | 13 | func Test_SimulateMultiOnline(t *testing.T) { 14 | ctx := ccontext.WithOperationID(context.Background(), "TEST_ROOT") 15 | userIDList := []string{"1", "2"} 16 | metaManager := module.NewMetaManager(APIADDR, SECRET, MANAGERUSERID) 17 | userManager := metaManager.NewUserManager() 18 | serverTime, err := metaManager.GetServerTime() 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | offset := serverTime - utils.GetCurrentTimestampByMill() 23 | sdk_user_simulator.SetServerTimeOffset(offset) 24 | for _, userID := range userIDList { 25 | token, err := userManager.GetToken(userID, int32(PLATFORMID)) 26 | if err != nil { 27 | log.ZError(ctx, "get token failed, err: %v", err, "userID", userID) 28 | continue 29 | } 30 | err = sdk_user_simulator.InitSDKAndLogin(userID, token) 31 | if err != nil { 32 | log.ZError(ctx, "login failed, err: %v", err, "userID", userID) 33 | } else { 34 | log.ZDebug(ctx, "login success, userID: %v", "userID", userID) 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /msgtest/module/api_msg_sender.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 8 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 9 | "github.com/openimsdk/protocol/constant" 10 | "github.com/openimsdk/protocol/msg" 11 | "github.com/openimsdk/protocol/sdkws" 12 | ) 13 | 14 | type ApiMsgSender struct { 15 | *MetaManager 16 | } 17 | 18 | type SendMsgReq struct { 19 | RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"` 20 | SendMsg 21 | } 22 | 23 | type SendMsg struct { 24 | SendID string `json:"sendID" binding:"required"` 25 | GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"` 26 | SenderNickname string `json:"senderNickname"` 27 | SenderFaceURL string `json:"senderFaceURL"` 28 | SenderPlatformID int32 `json:"senderPlatformID"` 29 | Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` 30 | ContentType int32 `json:"contentType" binding:"required"` 31 | SessionType int32 `json:"sessionType" binding:"required"` 32 | IsOnlineOnly bool `json:"isOnlineOnly"` 33 | NotOfflinePush bool `json:"notOfflinePush"` 34 | OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"` 35 | } 36 | 37 | func (a *ApiMsgSender) SendMsg(sendID, recvID string, index int) error { 38 | content := fmt.Sprintf("this is test msg user %s to user %s, index: %d", sendID, recvID, index) 39 | text := sdk_struct.TextElem{Content: content} 40 | req := &SendMsgReq{ 41 | RecvID: recvID, 42 | SendMsg: SendMsg{ 43 | SendID: sendID, 44 | SenderPlatformID: constant.WindowsPlatformID, 45 | ContentType: constant.Text, 46 | SessionType: constant.SingleChatType, 47 | Content: map[string]interface{}{"content": utils.StructToJsonString(text)}, 48 | }, 49 | } 50 | var resp msg.SendMsgResp 51 | return a.postWithCtx(api.SendMsg.Route(), req, &resp) 52 | } 53 | -------------------------------------------------------------------------------- /msgtest/module/friend_manager.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 5 | "github.com/openimsdk/protocol/relation" 6 | ) 7 | 8 | type TestFriendManager struct { 9 | *MetaManager 10 | } 11 | 12 | func (t *TestFriendManager) ImportFriends(ownerUserID string, friendUserIDs []string) error { 13 | req := &relation.ImportFriendReq{ 14 | OwnerUserID: ownerUserID, 15 | FriendUserIDs: friendUserIDs, 16 | } 17 | return t.postWithCtx(api.ImportFriendList.Route(), &req, nil) 18 | } 19 | -------------------------------------------------------------------------------- /msgtest/module/group_manager.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/constant" 8 | "time" 9 | 10 | "github.com/openimsdk/protocol/group" 11 | "github.com/openimsdk/protocol/sdkws" 12 | ) 13 | 14 | type TestGroupManager struct { 15 | *MetaManager 16 | } 17 | 18 | func (t *TestGroupManager) GenGroupID(prefix string) string { 19 | return fmt.Sprintf("%s_test_group_id_%d", prefix, time.Now().UnixNano()) 20 | } 21 | 22 | func (t *TestGroupManager) CreateGroup(groupID string, groupName string, ownerUserID string, userIDs []string) error { 23 | const batch = 2000 24 | var memberUserIDs []string 25 | if len(userIDs) > batch { 26 | memberUserIDs = userIDs[:batch] 27 | } else { 28 | memberUserIDs = userIDs 29 | } 30 | req := &group.CreateGroupReq{ 31 | MemberUserIDs: memberUserIDs, 32 | OwnerUserID: ownerUserID, 33 | GroupInfo: &sdkws.GroupInfo{ 34 | GroupID: groupID, 35 | GroupName: groupName, 36 | GroupType: constant.WorkingGroup, 37 | CreatorUserID: ownerUserID, 38 | }, 39 | } 40 | resp := &group.CreateGroupResp{} 41 | if err := t.postWithCtx(api.CreateGroup.Route(), &req, &resp); err != nil { 42 | return err 43 | } 44 | if len(userIDs) > batch { 45 | num := len(userIDs) / batch 46 | if len(userIDs)%batch != 0 { 47 | num++ 48 | } 49 | for i := 1; i < num; i++ { 50 | start := batch * i 51 | end := batch*i + batch 52 | if len(userIDs) < end { 53 | end = len(userIDs) 54 | } 55 | req := map[string]any{ 56 | "groupID": groupID, 57 | "invitedUserIDs": userIDs[start:end], 58 | "reason": "test", 59 | } 60 | resp := struct{}{} 61 | if err := t.postWithCtx(api.InviteUserToGroup.Route(), req, &resp); err != nil { 62 | return err 63 | } 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | func (t *TestGroupManager) InviteUserToGroup(ctx context.Context, groupID string, invitedUserIDs []string) error { 70 | req := &group.InviteUserToGroupReq{ 71 | GroupID: groupID, 72 | InvitedUserIDs: invitedUserIDs, 73 | } 74 | resp := &group.InviteUserToGroupResp{} 75 | return t.postWithCtx(api.InviteUserToGroup.Route(), &req, &resp) 76 | } 77 | -------------------------------------------------------------------------------- /msgtest/module/user_manager.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/openimsdk/openim-sdk-core/v3/pkg/api" 7 | "github.com/openimsdk/protocol/sdkws" 8 | userPB "github.com/openimsdk/protocol/user" 9 | ) 10 | 11 | type TestUserManager struct { 12 | *MetaManager 13 | } 14 | 15 | func (t *TestUserManager) GenUserIDs(num int) (userIDs []string) { 16 | for i := 0; i < num; i++ { 17 | userIDs = append(userIDs, fmt.Sprintf("testv3new_%d", i)) 18 | } 19 | return userIDs 20 | } 21 | 22 | func (t *TestUserManager) GenUserIDsWithPrefix(num int, prefix string) (userIDs []string) { 23 | for i := 0; i < num; i++ { 24 | userIDs = append(userIDs, fmt.Sprintf("%s%d", prefix, i)) 25 | } 26 | return userIDs 27 | } 28 | func (t *TestUserManager) GenSEUserIDsWithPrefix(start, end int, prefix string) (userIDs []string) { 29 | for i := start; i < end; i++ { 30 | userIDs = append(userIDs, fmt.Sprintf("%s%d", prefix, i)) 31 | } 32 | return userIDs 33 | } 34 | 35 | func (t *TestUserManager) RegisterUsers(userIDs ...string) error { 36 | var users []*sdkws.UserInfo 37 | for _, userID := range userIDs { 38 | users = append(users, &sdkws.UserInfo{UserID: userID, Nickname: userID}) 39 | } 40 | return t.postWithCtx(api.UserRegister.Route(), &userPB.UserRegisterReq{ 41 | Users: users, 42 | }, nil) 43 | } 44 | 45 | func (t *TestUserManager) GetToken(userID string, platformID int32) (string, error) { 46 | return t.getUserToken(userID, platformID) 47 | } 48 | -------------------------------------------------------------------------------- /open_im_sdk/apicb.go: -------------------------------------------------------------------------------- 1 | package open_im_sdk 2 | 3 | import ( 4 | "context" 5 | "sync/atomic" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/common" 9 | "github.com/openimsdk/tools/errs" 10 | "github.com/openimsdk/tools/log" 11 | ) 12 | 13 | type apiErrCallback struct { 14 | loginMgrCh chan common.Cmd2Value 15 | listener func() open_im_sdk_callback.OnConnListener 16 | tokenExpiredState int32 17 | kickedOfflineState int32 18 | tokenInvalidState int32 19 | } 20 | 21 | func (c *apiErrCallback) OnError(ctx context.Context, err error) { 22 | if err == nil { 23 | return 24 | } 25 | codeErr, ok := errs.Unwrap(err).(errs.CodeError) 26 | if !ok { 27 | log.ZError(ctx, "OnError callback not CodeError", err) 28 | return 29 | } 30 | log.ZError(ctx, "OnError callback CodeError", err, "code", codeErr.Code(), "msg", codeErr.Msg(), "detail", codeErr.Detail()) 31 | switch codeErr.Code() { 32 | case 33 | errs.TokenExpiredError: 34 | if atomic.CompareAndSwapInt32(&c.tokenExpiredState, 0, 1) { 35 | log.ZError(ctx, "OnUserTokenExpired callback", err) 36 | c.listener().OnUserTokenExpired() 37 | _ = common.DispatchLogout(ctx, c.loginMgrCh) 38 | } 39 | case 40 | errs.TokenInvalidError, 41 | errs.TokenMalformedError, 42 | errs.TokenNotValidYetError, 43 | errs.TokenUnknownError, 44 | errs.TokenNotExistError: 45 | if atomic.CompareAndSwapInt32(&c.tokenInvalidState, 0, 1) { 46 | log.ZError(ctx, "OnUserTokenInvalid callback", err) 47 | c.listener().OnUserTokenInvalid(err.Error()) 48 | _ = common.DispatchLogout(ctx, c.loginMgrCh) 49 | } 50 | 51 | case errs.TokenKickedError: 52 | if atomic.CompareAndSwapInt32(&c.kickedOfflineState, 0, 1) { 53 | log.ZError(ctx, "OnKickedOffline callback", err) 54 | c.listener().OnKickedOffline() 55 | _ = common.DispatchLogout(ctx, c.loginMgrCh) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /open_im_sdk/listener.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package open_im_sdk 16 | 17 | import ( 18 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 19 | ) 20 | 21 | func SetGroupListener(listener open_im_sdk_callback.OnGroupListener) { 22 | listenerCall(IMUserContext.SetGroupListener, listener) 23 | } 24 | 25 | func SetConversationListener(listener open_im_sdk_callback.OnConversationListener) { 26 | listenerCall(IMUserContext.SetConversationListener, listener) 27 | } 28 | 29 | func SetAdvancedMsgListener(listener open_im_sdk_callback.OnAdvancedMsgListener) { 30 | listenerCall(IMUserContext.SetAdvancedMsgListener, listener) 31 | } 32 | 33 | func SetUserListener(listener open_im_sdk_callback.OnUserListener) { 34 | listenerCall(IMUserContext.SetUserListener, listener) 35 | 36 | } 37 | 38 | func SetFriendListener(listener open_im_sdk_callback.OnFriendshipListener) { 39 | listenerCall(IMUserContext.SetFriendshipListener, listener) 40 | } 41 | 42 | func SetCustomBusinessListener(listener open_im_sdk_callback.OnCustomBusinessListener) { 43 | listenerCall(IMUserContext.SetCustomBusinessListener, listener) 44 | } 45 | 46 | func SetMessageKvInfoListener(listener open_im_sdk_callback.OnMessageKvInfoListener) { 47 | listenerCall(IMUserContext.SetMessageKvInfoListener, listener) 48 | } 49 | -------------------------------------------------------------------------------- /open_im_sdk/online.go: -------------------------------------------------------------------------------- 1 | package open_im_sdk 2 | 3 | import ( 4 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 5 | ) 6 | 7 | // SubscribeUsersStatus Presence status of subscribed users. 8 | func SubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) { 9 | call(callback, operationID, IMUserContext.LongConnMgr().SubscribeUsersStatus, userIDs) 10 | } 11 | 12 | // UnsubscribeUsersStatus Unsubscribe a user's presence. 13 | func UnsubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) { 14 | call(callback, operationID, IMUserContext.LongConnMgr().UnsubscribeUsersStatus, userIDs) 15 | } 16 | 17 | // GetSubscribeUsersStatus Get the online status of subscribers. 18 | func GetSubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string) { 19 | call(callback, operationID, IMUserContext.LongConnMgr().GetSubscribeUsersStatus) 20 | } 21 | 22 | // GetUserStatus Get the online status of users. 23 | func GetUserStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) { 24 | call(callback, operationID, IMUserContext.LongConnMgr().SubscribeUsersStatus, userIDs) 25 | } 26 | -------------------------------------------------------------------------------- /open_im_sdk/third.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package open_im_sdk 16 | 17 | import ( 18 | "github.com/openimsdk/openim-sdk-core/v3/internal/third/file" 19 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 20 | "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs" 21 | ) 22 | 23 | func UpdateFcmToken(callback open_im_sdk_callback.Base, operationID, fcmToken string, expireTime int64) { 24 | call(callback, operationID, IMUserContext.Third().UpdateFcmToken, fcmToken, expireTime) 25 | } 26 | 27 | func SetAppBadge(callback open_im_sdk_callback.Base, operationID string, appUnreadCount int32) { 28 | call(callback, operationID, IMUserContext.Third().SetAppBadge, appUnreadCount) 29 | } 30 | 31 | func UploadLogs(callback open_im_sdk_callback.Base, operationID string, line int, ex string, progress open_im_sdk_callback.UploadLogProgress) { 32 | call(callback, operationID, IMUserContext.Third().UploadLogs, line, ex, progress) 33 | } 34 | 35 | func Logs(callback open_im_sdk_callback.Base, operationID string, logLevel int, file string, line int, msgs string, err string, keyAndValue string) { 36 | if IMUserContext == nil || IMUserContext.Third() == nil { 37 | callback.OnError(sdkerrs.SdkInternalError, "sdk not init") 38 | return 39 | } 40 | call(callback, operationID, IMUserContext.Third().Log, logLevel, file, line, msgs, err, keyAndValue) 41 | } 42 | 43 | func UploadFile(callback open_im_sdk_callback.Base, operationID string, req string, progress open_im_sdk_callback.UploadFileCallback) { 44 | call(callback, operationID, IMUserContext.File().UploadFile, req, file.UploadFileCallback(progress)) 45 | } 46 | -------------------------------------------------------------------------------- /open_im_sdk/user.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package open_im_sdk 16 | 17 | import ( 18 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 19 | ) 20 | 21 | func GetUsersInfo(callback open_im_sdk_callback.Base, operationID string, userIDs string) { 22 | call(callback, operationID, IMUserContext.User().GetUsersInfo, userIDs) 23 | } 24 | 25 | // SetSelfInfo sets the user's own information. 26 | func SetSelfInfo(callback open_im_sdk_callback.Base, operationID string, userInfo string) { 27 | call(callback, operationID, IMUserContext.User().SetSelfInfo, userInfo) 28 | } 29 | 30 | // GetSelfUserInfo obtains the user's own information. 31 | func GetSelfUserInfo(callback open_im_sdk_callback.Base, operationID string) { 32 | call(callback, operationID, IMUserContext.User().GetSelfUserInfo) 33 | } 34 | 35 | func GetUserClientConfig(callback open_im_sdk_callback.Base, operationID string) { 36 | call(callback, operationID, IMUserContext.User().GetUserClientConfig) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/api/fn.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "reflect" 7 | 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/network" 9 | "github.com/openimsdk/protocol/sdkws" 10 | ) 11 | 12 | func newApi[Req, Resp any](api string) Api[Req, Resp] { 13 | return Api[Req, Resp]{ 14 | api: api, 15 | } 16 | } 17 | 18 | type Api[Req, Resp any] struct { 19 | api string 20 | } 21 | 22 | func (a Api[Req, Resp]) Invoke(ctx context.Context, req *Req) (*Resp, error) { 23 | var resp Resp 24 | if err := network.ApiPost(ctx, a.api, req, &resp); err != nil { 25 | return nil, err 26 | } 27 | return &resp, nil 28 | } 29 | 30 | func (a Api[Req, Resp]) Execute(ctx context.Context, req *Req) error { 31 | _, err := a.Invoke(ctx, req) 32 | return err 33 | } 34 | 35 | func (a Api[Req, Resp]) Route() string { 36 | return a.api 37 | } 38 | 39 | // ExtractField is a generic function that extracts a field from the response of a given function. 40 | func ExtractField[A, B, C any](ctx context.Context, fn func(ctx context.Context, req *A) (*B, error), req *A, get func(*B) C) (C, error) { 41 | resp, err := fn(ctx, req) 42 | if err != nil { 43 | var c C 44 | return c, err 45 | } 46 | return get(resp), nil 47 | } 48 | 49 | type pagination interface { 50 | GetPagination() *sdkws.RequestPagination 51 | } 52 | 53 | func Page[Req pagination, Resp any, Elem any](ctx context.Context, req Req, api func(ctx context.Context, req Req) (*Resp, error), fn func(*Resp) []Elem) ([]Elem, error) { 54 | if req.GetPagination() == nil { 55 | vof := reflect.ValueOf(req) 56 | for { 57 | if vof.Kind() == reflect.Ptr { 58 | vof = vof.Elem() 59 | } else { 60 | break 61 | } 62 | } 63 | if vof.Kind() != reflect.Struct { 64 | return nil, fmt.Errorf("request is not a struct") 65 | } 66 | fof := vof.FieldByName("Pagination") 67 | if !fof.IsValid() { 68 | return nil, fmt.Errorf("request is not valid Pagination field") 69 | } 70 | fof.Set(reflect.ValueOf(&sdkws.RequestPagination{})) 71 | } 72 | if req.GetPagination().PageNumber < 0 { 73 | req.GetPagination().PageNumber = 0 74 | } 75 | if req.GetPagination().ShowNumber <= 0 { 76 | req.GetPagination().ShowNumber = 200 77 | } 78 | var result []Elem 79 | for i := int32(0); ; i++ { 80 | req.GetPagination().PageNumber = i + 1 81 | resp, err := api(ctx, req) 82 | if err != nil { 83 | return nil, err 84 | } 85 | elems := fn(resp) 86 | result = append(result, elems...) 87 | if len(elems) < int(req.GetPagination().ShowNumber) { 88 | break 89 | } 90 | } 91 | return result, nil 92 | } 93 | -------------------------------------------------------------------------------- /pkg/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "sync" 4 | 5 | func NewCache[K comparable, V any]() *Cache[K, V] { 6 | return &Cache[K, V]{} 7 | } 8 | 9 | // Cache is a Generic sync.Map structure. 10 | type Cache[K comparable, V any] struct { 11 | m sync.Map 12 | } 13 | 14 | // Load returns the value stored in the map for a key, or nil if no value is present. 15 | func (c *Cache[K, V]) Load(key K) (value V, ok bool) { 16 | rawValue, ok := c.m.Load(key) 17 | if !ok { 18 | return 19 | } 20 | return rawValue.(V), ok 21 | } 22 | 23 | // Store sets the value for a key. 24 | func (c *Cache[K, V]) Store(key K, value V) { 25 | c.m.Store(key, value) 26 | } 27 | 28 | // StoreWithFunc stores the value for a key only if the provided condition function returns true. 29 | func (c *Cache[K, V]) StoreWithFunc(key K, value V, condition func(key K, value V) bool) { 30 | if condition(key, value) { 31 | c.m.Store(key, value) 32 | } 33 | } 34 | 35 | // StoreAll sets all value by f's key. 36 | func (c *Cache[K, V]) StoreAll(f func(value V) K, values []V) { 37 | for _, v := range values { 38 | c.m.Store(f(v), v) 39 | } 40 | } 41 | 42 | // LoadOrStore returns the existing value for the key if present. 43 | func (c *Cache[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { 44 | rawValue, loaded := c.m.LoadOrStore(key, value) 45 | return rawValue.(V), loaded 46 | } 47 | 48 | // Delete deletes the value for a key. 49 | func (c *Cache[K, V]) Delete(key K) { 50 | c.m.Delete(key) 51 | } 52 | 53 | // DeleteAll deletes all values. 54 | func (c *Cache[K, V]) DeleteAll() { 55 | c.m.Range(func(key, value interface{}) bool { 56 | c.m.Delete(key) 57 | return true 58 | }) 59 | } 60 | 61 | // DeleteCon deletes the value for a key only if the provided condition function returns true. 62 | func (c *Cache[K, V]) DeleteCon(condition func(key K, value V) bool) { 63 | c.m.Range(func(rawKey, rawValue interface{}) bool { 64 | if condition(rawKey.(K), rawValue.(V)) { 65 | c.m.Delete(rawKey) 66 | } 67 | return true // Continue iteration 68 | }) 69 | } 70 | 71 | // RangeAll returns all values in the map. 72 | func (c *Cache[K, V]) RangeAll() (values []V) { 73 | c.m.Range(func(rawKey, rawValue interface{}) bool { 74 | values = append(values, rawValue.(V)) 75 | return true 76 | }) 77 | return values 78 | } 79 | 80 | // RangeCon returns values in the map that satisfy condition f. 81 | func (c *Cache[K, V]) RangeCon(f func(key K, value V) bool) (values []V) { 82 | c.m.Range(func(rawKey, rawValue interface{}) bool { 83 | if f(rawKey.(K), rawValue.(V)) { 84 | values = append(values, rawValue.(V)) 85 | } 86 | return true 87 | }) 88 | return values 89 | } 90 | -------------------------------------------------------------------------------- /pkg/cache/conversation_seq_cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/openimsdk/tools/utils/stringutil" 7 | ) 8 | 9 | const ( 10 | ViewHistory = iota 11 | ViewSearch 12 | ) 13 | 14 | type ConversationSeqContextCache struct { 15 | *Cache[string, int64] 16 | } 17 | 18 | func NewConversationSeqContextCache() *ConversationSeqContextCache { 19 | return &ConversationSeqContextCache{Cache: NewCache[string, int64]()} 20 | } 21 | 22 | func (c ConversationSeqContextCache) Load(conversationID string, viewType int) (int64, bool) { 23 | return c.Cache.Load(c.getConversationViewTypeKey(conversationID, viewType)) 24 | 25 | } 26 | func (c ConversationSeqContextCache) Delete(conversationID string, viewType int) { 27 | c.Cache.Delete(c.getConversationViewTypeKey(conversationID, viewType)) 28 | 29 | } 30 | 31 | func (c ConversationSeqContextCache) Store(conversationID string, viewType int, thisEndSeq int64) { 32 | c.Cache.Store(c.getConversationViewTypeKey(conversationID, viewType), thisEndSeq) 33 | 34 | } 35 | 36 | func (c ConversationSeqContextCache) StoreWithFunc(conversationID string, viewType int, thisEndSeq int64, fn func(key string, value int64) bool) { 37 | 38 | c.Cache.StoreWithFunc(c.getConversationViewTypeKey(conversationID, viewType), thisEndSeq, fn) 39 | } 40 | 41 | func (c ConversationSeqContextCache) getConversationViewTypeKey(conversationID string, viewType int) string { 42 | return conversationID + "::viewType::" + stringutil.IntToString(viewType) 43 | } 44 | 45 | func (c ConversationSeqContextCache) DeleteByViewType(viewType int) { 46 | c.Cache.DeleteCon(func(key string, value int64) bool { 47 | return strings.Contains(key, "::viewType::"+stringutil.IntToString(viewType)) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/ccontext/context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ccontext 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "testing" 21 | ) 22 | 23 | func TestName(t *testing.T) { 24 | ctx := context.Background() 25 | conf := GlobalConfig{ 26 | UserID: "uid123", 27 | } 28 | ctx = WithInfo(ctx, &conf) 29 | operationID := "opid123" 30 | ctx = WithOperationID(ctx, operationID) 31 | 32 | fmt.Println("UserID:", Info(ctx).UserID()) 33 | fmt.Println("OperationID:", Info(ctx).OperationID()) 34 | if Info(ctx).UserID() != conf.UserID { 35 | t.Fatal("UserID not match") 36 | } 37 | 38 | if Info(ctx).OperationID() != operationID { 39 | t.Fatal("OperationID not match") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/cliconf/global.go: -------------------------------------------------------------------------------- 1 | package cliconf 2 | 3 | import "context" 4 | 5 | var globalClientConfig = &clientConfig{} 6 | 7 | func SetLoginUserID(userID string) { 8 | globalClientConfig.userID = userID 9 | globalClientConfig.ClearConfig() 10 | } 11 | 12 | func ClearConfig() { 13 | globalClientConfig.ClearConfig() 14 | } 15 | 16 | func GetClientConfig(ctx context.Context) (*ClientConfig, error) { 17 | return globalClientConfig.GetConfig(ctx) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/common/EventQueue.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type EventHandler func(ctx context.Context, event *Event) 8 | 9 | type LogCallback func(msg string, fields ...any) 10 | 11 | // EventQueue is responsible for concurrently consuming events from the PriorityQueue 12 | type EventQueue struct { 13 | queue *PriorityQueue 14 | } 15 | 16 | // NewEventQueue creates a worker pool instance 17 | func NewEventQueue(capacity int) *EventQueue { 18 | return &EventQueue{ 19 | queue: NewPriorityQueue(capacity), 20 | } 21 | } 22 | 23 | func (e *EventQueue) Produce(data any, priority int) (*Event, error) { 24 | event := &Event{Data: data, Priority: priority} 25 | err := e.queue.Push(event) 26 | return event, err 27 | } 28 | func (q *EventQueue) ProduceWithContext(ctx context.Context, data any, priority int) (*Event, error) { 29 | event := &Event{Data: data, Priority: priority} 30 | return event, q.queue.PushWithContext(ctx, event) 31 | } 32 | 33 | func (e *EventQueue) UpdatePriority(event *Event, newPriority int) error { 34 | return e.queue.UpdatePriority(event, newPriority) 35 | } 36 | 37 | func (e *EventQueue) ConsumeLoop(ctx context.Context, handle EventHandler, log LogCallback) { 38 | for { 39 | event, err := e.queue.PopWithContext(ctx) 40 | if err != nil { 41 | select { 42 | case <-ctx.Done(): 43 | log("ctx canceled", err) 44 | return 45 | default: 46 | log("pop error", err) 47 | continue 48 | } 49 | } 50 | handle(ctx, event) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/common/EventQueue_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEventQueue_ProduceAndConsume(t *testing.T) { 13 | var ( 14 | mu sync.Mutex 15 | handledOrder []string 16 | ) 17 | 18 | handler := func(ctx context.Context, event *Event) { 19 | mu.Lock() 20 | handledOrder = append(handledOrder, event.Data.(string)) 21 | mu.Unlock() 22 | } 23 | 24 | logCallback := func(msg string, fields ...any) { 25 | 26 | } 27 | 28 | ctx, cancel := context.WithCancel(context.Background()) 29 | defer cancel() 30 | 31 | eq := NewEventQueue(10) 32 | 33 | _, _ = eq.Produce("low1", 1) 34 | _, _ = eq.Produce("medium", 5) 35 | _, _ = eq.Produce("low2", 1) 36 | _, _ = eq.Produce("high", 10) 37 | 38 | go eq.ConsumeLoop(ctx, handler, logCallback) 39 | 40 | time.Sleep(500 * time.Millisecond) 41 | cancel() 42 | 43 | mu.Lock() 44 | defer mu.Unlock() 45 | assert.Equal(t, []string{"high", "medium", "low1", "low2"}, handledOrder) 46 | } 47 | 48 | func TestEventQueue_CancelContext(t *testing.T) { 49 | called := false 50 | 51 | handler := func(ctx context.Context, event *Event) { 52 | called = true 53 | } 54 | 55 | logCallback := func(msg string, fields ...any) {} 56 | 57 | ctx, cancel := context.WithCancel(context.Background()) 58 | eq := NewEventQueue(1) 59 | 60 | go eq.ConsumeLoop(ctx, handler, logCallback) 61 | 62 | cancel() 63 | time.Sleep(100 * time.Millisecond) 64 | 65 | assert.False(t, called) 66 | } 67 | 68 | func TestEventQueue_UpdatePriority(t *testing.T) { 69 | var mu sync.Mutex 70 | var results []string 71 | 72 | handler := func(ctx context.Context, event *Event) { 73 | mu.Lock() 74 | results = append(results, event.Data.(string)) 75 | mu.Unlock() 76 | } 77 | 78 | logCallback := func(msg string, fields ...any) {} 79 | 80 | ctx, cancel := context.WithCancel(context.Background()) 81 | defer cancel() 82 | 83 | eq := NewEventQueue(5) 84 | go eq.ConsumeLoop(ctx, handler, logCallback) 85 | 86 | e1, _ := eq.Produce("a", 1) 87 | _, _ = eq.Produce("b", 2) 88 | 89 | time.Sleep(50 * time.Millisecond) 90 | _ = eq.UpdatePriority(e1, 5) 91 | 92 | time.Sleep(500 * time.Millisecond) 93 | cancel() 94 | 95 | mu.Lock() 96 | defer mu.Unlock() 97 | assert.Equal(t, []string{"a", "b"}, results) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/content_type/content_type.go: -------------------------------------------------------------------------------- 1 | package content_type 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | var ext = map[string]string{ 8 | "html": "text/html", 9 | "htm": "text/html", 10 | "css": "text/css", 11 | "js": "application/javascript", 12 | "json": "application/json", 13 | "xml": "application/xml", 14 | "png": "image/png", 15 | "jpg": "image/jpeg", 16 | "jpeg": "image/jpeg", 17 | "gif": "image/gif", 18 | "bmp": "image/bmp", 19 | "tif": "image/tiff", 20 | "tiff": "image/tiff", 21 | "ico": "image/x-icon", 22 | "svg": "image/svg+xml", 23 | "webp": "image/webp", 24 | "mp4": "video/mp4", 25 | "avi": "video/x-msvideo", 26 | "mkv": "video/x-matroska", 27 | "mov": "video/quicktime", 28 | "wmv": "video/x-ms-wmv", 29 | "flv": "video/x-flv", 30 | "webm": "video/webm", 31 | "3gp": "video/3gpp", 32 | "m4a": "audio/mp4", 33 | "mp3": "audio/mpeg", 34 | "wav": "audio/wav", 35 | "ogg": "audio/ogg", 36 | "aac": "audio/aac", 37 | "wma": "audio/x-ms-wma", 38 | "flac": "audio/flac", 39 | "mid": "audio/midi", 40 | "midi": "audio/midi", 41 | "weba": "audio/webm", 42 | "pdf": "application/pdf", 43 | "doc": "application/msword", 44 | "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 45 | "xls": "application/vnd.ms-excel", 46 | "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 47 | "ppt": "application/vnd.ms-powerpoint", 48 | "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", 49 | "txt": "text/plain", 50 | "csv": "text/csv", 51 | "zip": "application/zip", 52 | "rar": "application/x-rar-compressed", 53 | "tar": "application/x-tar", 54 | "gz": "application/gzip", 55 | "exe": "application/x-msdownload", 56 | "msi": "application/x-msi", 57 | "deb": "application/x-debian-package", 58 | "rpm": "application/x-redhat-package-manager", 59 | "sh": "application/x-sh", 60 | "bat": "application/bat", 61 | "py": "application/x-python", 62 | "java": "text/x-java-source", 63 | "c": "text/x-csrc", 64 | "cpp": "text/x-c++src", 65 | "h": "text/x-chdr", 66 | "hpp": "text/x-c++hdr", 67 | "php": "application/x-php", 68 | "asp": "application/x-asp", 69 | "jsp": "application/x-jsp", 70 | "dll": "application/x-msdownload", 71 | "jar": "application/java-archive", 72 | "war": "application/java-archive", 73 | "ear": "application/java-archive", 74 | } 75 | 76 | func GetType(val ...string) string { 77 | for _, s := range val { 78 | if len(s) > 1 { 79 | if i := strings.IndexByte(s, '/'); i > 0 && i < len(s)-1 { 80 | return s 81 | } 82 | if strings.HasPrefix(s, ".") { 83 | s = s[1:] 84 | } 85 | if val, ok := ext[s]; ok { 86 | return val 87 | } 88 | if val, ok := ext[strings.ToLower(s)]; ok { 89 | return val 90 | } 91 | } 92 | } 93 | return "" 94 | } 95 | -------------------------------------------------------------------------------- /pkg/db/admin_group_request_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | "errors" 23 | 24 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 25 | "github.com/openimsdk/tools/errs" 26 | ) 27 | 28 | func (d *DataBase) InsertAdminGroupRequest(ctx context.Context, groupRequest *model_struct.LocalAdminGroupRequest) error { 29 | d.mRWMutex.Lock() 30 | defer d.mRWMutex.Unlock() 31 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupRequest).Error, "InsertAdminGroupRequest failed") 32 | } 33 | 34 | func (d *DataBase) DeleteAdminGroupRequest(ctx context.Context, groupID, userID string) error { 35 | d.mRWMutex.Lock() 36 | defer d.mRWMutex.Unlock() 37 | return errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id=? and user_id=?", groupID, userID).Delete(&model_struct.LocalAdminGroupRequest{}).Error, "DeleteAdminGroupRequest failed") 38 | } 39 | 40 | func (d *DataBase) UpdateAdminGroupRequest(ctx context.Context, groupRequest *model_struct.LocalAdminGroupRequest) error { 41 | d.mRWMutex.Lock() 42 | defer d.mRWMutex.Unlock() 43 | t := d.conn.WithContext(ctx).Model(groupRequest).Select("*").Updates(*groupRequest) 44 | if t.RowsAffected == 0 { 45 | return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update") 46 | } 47 | return errs.Wrap(t.Error) 48 | } 49 | 50 | func (d *DataBase) GetAdminGroupApplication(ctx context.Context) ([]*model_struct.LocalAdminGroupRequest, error) { 51 | d.mRWMutex.RLock() 52 | defer d.mRWMutex.RUnlock() 53 | var groupRequestList []*model_struct.LocalAdminGroupRequest 54 | err := errs.Wrap(d.conn.WithContext(ctx).Order("create_time DESC").Find(&groupRequestList).Error) 55 | if err != nil { 56 | return nil, errs.Wrap(err) 57 | } 58 | return groupRequestList, nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/db/app_version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | 23 | "gorm.io/gorm" 24 | 25 | "github.com/openimsdk/tools/errs" 26 | 27 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 28 | ) 29 | 30 | func (d *DataBase) GetAppSDKVersion(ctx context.Context) (*model_struct.LocalAppSDKVersion, error) { 31 | var appVersion model_struct.LocalAppSDKVersion 32 | err := d.conn.WithContext(ctx).Take(&appVersion).Error 33 | if err == gorm.ErrRecordNotFound { 34 | err = errs.ErrRecordNotFound 35 | } 36 | return &appVersion, errs.Wrap(err) 37 | } 38 | 39 | func (d *DataBase) SetAppSDKVersion(ctx context.Context, appVersion *model_struct.LocalAppSDKVersion) error { 40 | var exist model_struct.LocalAppSDKVersion 41 | err := d.conn.WithContext(ctx).First(&exist).Error 42 | if err == gorm.ErrRecordNotFound { 43 | if createErr := d.conn.WithContext(ctx).Create(appVersion).Error; createErr != nil { 44 | return errs.Wrap(createErr) 45 | } 46 | return nil 47 | } else if err != nil { 48 | return errs.Wrap(err) 49 | } 50 | 51 | if updateErr := d.conn.WithContext(ctx).Model(&exist).Updates(appVersion).Error; updateErr != nil { 52 | return errs.Wrap(updateErr) 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/db/app_version_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 8 | "github.com/openimsdk/tools/log" 9 | ) 10 | 11 | func Test_GetAppSDKVersion(t *testing.T) { 12 | ctx := context.Background() 13 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 14 | if err != nil { 15 | return 16 | } 17 | // log.ZError(ctx, "DB err test", nil, "key", "vale") 18 | 19 | info, err := db.GetAppSDKVersion(ctx) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | log.ZDebug(ctx, "info is ", "info", info) 24 | } 25 | 26 | func Test_SetAppSDKVersion(t *testing.T) { 27 | ctx := context.Background() 28 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 29 | if err != nil { 30 | return 31 | } 32 | 33 | newVersion := &model_struct.LocalAppSDKVersion{ 34 | Version: "4.0.0", 35 | } 36 | 37 | err = db.SetAppSDKVersion(ctx, newVersion) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/db/chat_log_model_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func TestGetLatestValidateServerMessage(t *testing.T) { 9 | ctx := context.Background() 10 | db, err := NewDataBase(ctx, "1695766238", "../../", 6) 11 | if err != nil { 12 | return 13 | } 14 | 15 | message, err := db.GetLatestValidServerMessage(ctx, "sg_93606743", 1710774468519, true) 16 | 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | t.Log("message", message) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/db/conversation_unread_message_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 24 | "github.com/openimsdk/tools/errs" 25 | ) 26 | 27 | func (d *DataBase) BatchInsertConversationUnreadMessageList(ctx context.Context, messageList []*model_struct.LocalConversationUnreadMessage) error { 28 | if messageList == nil { 29 | return nil 30 | } 31 | d.mRWMutex.Lock() 32 | defer d.mRWMutex.Unlock() 33 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(messageList).Error, "BatchInsertConversationUnreadMessageList failed") 34 | } 35 | func (d *DataBase) DeleteConversationUnreadMessageList(ctx context.Context, conversationID string, sendTime int64) int64 { 36 | d.mRWMutex.Lock() 37 | defer d.mRWMutex.Unlock() 38 | return d.conn.WithContext(ctx).Where("conversation_id = ? and send_time <= ?", conversationID, sendTime).Delete(&model_struct.LocalConversationUnreadMessage{}).RowsAffected 39 | } 40 | -------------------------------------------------------------------------------- /pkg/db/group_model_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 8 | ) 9 | 10 | func Test_GetGroupMemberListByUserIDs(t *testing.T) { 11 | ctx := context.Background() 12 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 13 | if err != nil { 14 | return 15 | } 16 | // log.ZError(ctx, "DB err test", nil, "key", "vale") 17 | 18 | _, err = db.GetGroupMemberListByUserIDs(ctx, "337845818", 0, []string{"123124123", "33333333"}) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | 23 | } 24 | func Test_BatchInsertGroup(t *testing.T) { 25 | 26 | ctx := context.Background() 27 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 28 | if err != nil { 29 | return 30 | } 31 | // log.ZError(ctx, "DB err test", nil, "key", "vale") 32 | 33 | localGroups := []*model_struct.LocalGroup{ 34 | { 35 | GroupID: "1234567", 36 | GroupName: "test1234", 37 | Notification: "", 38 | Introduction: "", 39 | FaceURL: "", 40 | CreateTime: 1666777417, 41 | Status: 0, 42 | CreatorUserID: "", 43 | GroupType: 0, 44 | OwnerUserID: "", 45 | MemberCount: 0, 46 | Ex: "", 47 | AttachedInfo: "", 48 | NeedVerification: 0, 49 | LookMemberInfo: 0, 50 | ApplyMemberFriend: 0, 51 | NotificationUpdateTime: 0, 52 | NotificationUserID: "", 53 | }, 54 | { 55 | GroupID: "1234568", 56 | GroupName: "test5678", 57 | Notification: "New Notification", 58 | Introduction: "This is a test group", 59 | FaceURL: "https://example.com/face.png", 60 | CreateTime: 1666777420, 61 | Status: 1, 62 | CreatorUserID: "user123", 63 | GroupType: 1, 64 | OwnerUserID: "user456", 65 | MemberCount: 10, 66 | Ex: "ex", 67 | AttachedInfo: "Attach", 68 | NeedVerification: 1, 69 | LookMemberInfo: 1, 70 | ApplyMemberFriend: 1, 71 | NotificationUpdateTime: 1666777425, 72 | NotificationUserID: "user789", 73 | }, 74 | } 75 | 76 | err = db.BatchInsertGroup(ctx, localGroups) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | } 81 | 82 | func Test_DeleteAllGroup(t *testing.T) { 83 | ctx := context.Background() 84 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 85 | if err != nil { 86 | return 87 | } 88 | // log.ZError(ctx, "DB err test", nil, "key", "vale") 89 | 90 | err = db.DeleteAllGroup(ctx) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /pkg/db/group_request_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | "errors" 23 | 24 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 25 | "github.com/openimsdk/tools/errs" 26 | ) 27 | 28 | func (d *DataBase) InsertGroupRequest(ctx context.Context, groupRequest *model_struct.LocalGroupRequest) error { 29 | d.mRWMutex.Lock() 30 | defer d.mRWMutex.Unlock() 31 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupRequest).Error, "InsertGroupRequest failed") 32 | } 33 | func (d *DataBase) DeleteGroupRequest(ctx context.Context, groupID, userID string) error { 34 | d.mRWMutex.Lock() 35 | defer d.mRWMutex.Unlock() 36 | return errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id=? and user_id=?", groupID, userID).Delete(&model_struct.LocalGroupRequest{}).Error, "DeleteGroupRequest failed") 37 | } 38 | func (d *DataBase) UpdateGroupRequest(ctx context.Context, groupRequest *model_struct.LocalGroupRequest) error { 39 | d.mRWMutex.Lock() 40 | defer d.mRWMutex.Unlock() 41 | t := d.conn.WithContext(ctx).Model(groupRequest).Select("*").Updates(*groupRequest) 42 | if t.RowsAffected == 0 { 43 | return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update") 44 | } 45 | return errs.Wrap(t.Error) 46 | } 47 | 48 | func (d *DataBase) GetSendGroupApplication(ctx context.Context) ([]*model_struct.LocalGroupRequest, error) { 49 | d.mRWMutex.RLock() 50 | defer d.mRWMutex.RUnlock() 51 | var groupRequestList []*model_struct.LocalGroupRequest 52 | return groupRequestList, errs.Wrap(d.conn.WithContext(ctx).Order("create_time DESC").Find(&groupRequestList).Error) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/db/notification_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 24 | "github.com/openimsdk/tools/errs" 25 | ) 26 | 27 | func (d *DataBase) SetNotificationSeq(ctx context.Context, conversationID string, seq int64) error { 28 | d.mRWMutex.Lock() 29 | defer d.mRWMutex.Unlock() 30 | cursor := d.conn.WithContext(ctx).Model(&model_struct.NotificationSeqs{}).Where("conversation_id = ?", conversationID).Updates(map[string]interface{}{"seq": seq}) 31 | if cursor.Error != nil { 32 | return errs.WrapMsg(cursor.Error, "Updates failed") 33 | } 34 | if cursor.RowsAffected == 0 { 35 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(&model_struct.NotificationSeqs{ConversationID: conversationID, Seq: seq}).Error, "Create failed") 36 | } 37 | return nil 38 | } 39 | 40 | func (d *DataBase) BatchInsertNotificationSeq(ctx context.Context, notificationSeqs []*model_struct.NotificationSeqs) error { 41 | d.mRWMutex.Lock() 42 | defer d.mRWMutex.Unlock() 43 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(notificationSeqs).Error, "BatchInsertNotificationSeq failed") 44 | } 45 | 46 | func (d *DataBase) GetNotificationAllSeqs(ctx context.Context) ([]*model_struct.NotificationSeqs, error) { 47 | d.mRWMutex.RLock() 48 | defer d.mRWMutex.RUnlock() 49 | var seqs []*model_struct.NotificationSeqs 50 | return seqs, errs.WrapMsg(d.conn.WithContext(ctx).Where("1=1").Find(&seqs).Error, "GetNotificationAllSeqs failed") 51 | } 52 | -------------------------------------------------------------------------------- /pkg/db/notification_model_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 8 | ) 9 | 10 | func Test_BatchInsertNotificationSeq(t *testing.T) { 11 | ctx := context.Background() 12 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 13 | if err != nil { 14 | return 15 | } 16 | // log.ZError(ctx, "DB err test", nil, "key", "vale") 17 | notificationSeqs := []*model_struct.NotificationSeqs{ 18 | { 19 | ConversationID: "n_2882899447_69085919821", 20 | Seq: 2, 21 | }, 22 | { 23 | ConversationID: "n_2882899447_69085919822", 24 | Seq: 3, 25 | }, 26 | } 27 | err = db.BatchInsertNotificationSeq(ctx, notificationSeqs) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /pkg/db/sending_messages_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 24 | "github.com/openimsdk/tools/errs" 25 | ) 26 | 27 | func (d *DataBase) InsertSendingMessage(ctx context.Context, message *model_struct.LocalSendingMessages) error { 28 | d.mRWMutex.Lock() 29 | defer d.mRWMutex.Unlock() 30 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(message).Error, "InsertSendingMessage failed") 31 | } 32 | 33 | func (d *DataBase) DeleteSendingMessage(ctx context.Context, conversationID, clientMsgID string) error { 34 | d.mRWMutex.Lock() 35 | defer d.mRWMutex.Unlock() 36 | localSendingMessage := model_struct.LocalSendingMessages{ConversationID: conversationID, ClientMsgID: clientMsgID} 37 | return errs.WrapMsg(d.conn.WithContext(ctx).Delete(&localSendingMessage).Error, "DeleteSendingMessage failed") 38 | } 39 | func (d *DataBase) GetAllSendingMessages(ctx context.Context) (friendRequests []*model_struct.LocalSendingMessages, err error) { 40 | d.mRWMutex.RLock() 41 | defer d.mRWMutex.RUnlock() 42 | return friendRequests, errs.WrapMsg(d.conn.WithContext(ctx).Find(&friendRequests).Error, "GetAllSendingMessages failed") 43 | } 44 | -------------------------------------------------------------------------------- /pkg/db/seq_data_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 24 | "github.com/openimsdk/tools/errs" 25 | ) 26 | 27 | func (d *DataBase) GetMinSeq(ctx context.Context, ID string) (uint32, error) { 28 | d.mRWMutex.RLock() 29 | defer d.mRWMutex.RUnlock() 30 | var seqData model_struct.LocalSeq 31 | return seqData.MinSeq, errs.WrapMsg(d.conn.WithContext(ctx).First(&seqData).Error, "GetMinSeq failed") 32 | } 33 | 34 | func (d *DataBase) SetMinSeq(ctx context.Context, ID string, minSeq uint32) error { 35 | d.mRWMutex.Lock() 36 | defer d.mRWMutex.Unlock() 37 | seqData := model_struct.LocalSeq{ID: ID, MinSeq: minSeq} 38 | t := d.conn.WithContext(ctx).Updates(&seqData) 39 | if t.RowsAffected == 0 { 40 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(seqData).Error, "Updates failed") 41 | } else { 42 | return errs.WrapMsg(t.Error, "SetMinSeq failed") 43 | } 44 | } 45 | 46 | func (d *DataBase) GetUserMinSeq(ctx context.Context) (uint32, error) { 47 | return 0, nil 48 | } 49 | 50 | func (d *DataBase) GetGroupMinSeq(ctx context.Context, groupID string) (uint32, error) { 51 | return 0, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/db/table_master.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | // +build !js 3 | 4 | package db 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/openimsdk/tools/errs" 10 | ) 11 | 12 | func (d *DataBase) GetExistTables(ctx context.Context) ([]string, error) { 13 | d.mRWMutex.RLock() 14 | defer d.mRWMutex.RUnlock() 15 | var tables []string 16 | return tables, errs.Wrap(d.conn.WithContext(ctx).Raw("SELECT name FROM sqlite_master WHERE type='table'").Scan(&tables).Error) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /pkg/db/upload_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | "time" 23 | 24 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 25 | 26 | "github.com/openimsdk/tools/errs" 27 | ) 28 | 29 | func (d *DataBase) GetUpload(ctx context.Context, partHash string) (*model_struct.LocalUpload, error) { 30 | d.mRWMutex.Lock() 31 | defer d.mRWMutex.Unlock() 32 | var upload model_struct.LocalUpload 33 | err := d.conn.WithContext(ctx).Where("part_hash = ?", partHash).Take(&upload).Error 34 | if err != nil { 35 | return nil, errs.Wrap(err) 36 | } 37 | return &upload, nil 38 | } 39 | 40 | func (d *DataBase) InsertUpload(ctx context.Context, upload *model_struct.LocalUpload) error { 41 | d.mRWMutex.Lock() 42 | defer d.mRWMutex.Unlock() 43 | return errs.Wrap(d.conn.WithContext(ctx).Create(upload).Error) 44 | } 45 | 46 | func (d *DataBase) deleteUpload(ctx context.Context, partHash string) error { 47 | return errs.Wrap(d.conn.WithContext(ctx).Where("part_hash = ?", partHash).Delete(&model_struct.LocalUpload{}).Error) 48 | } 49 | 50 | func (d *DataBase) UpdateUpload(ctx context.Context, upload *model_struct.LocalUpload) error { 51 | d.mRWMutex.Lock() 52 | defer d.mRWMutex.Unlock() 53 | return errs.Wrap(d.conn.WithContext(ctx).Updates(upload).Error) 54 | } 55 | 56 | func (d *DataBase) DeleteUpload(ctx context.Context, partHash string) error { 57 | d.mRWMutex.Lock() 58 | defer d.mRWMutex.Unlock() 59 | return d.deleteUpload(ctx, partHash) 60 | } 61 | 62 | func (d *DataBase) DeleteExpireUpload(ctx context.Context) error { 63 | d.mRWMutex.Lock() 64 | defer d.mRWMutex.Unlock() 65 | var uploads []*model_struct.LocalUpload 66 | err := d.conn.WithContext(ctx).Where("expire_time <= ?", time.Now().UnixMilli()).Find(&uploads).Error 67 | if err != nil { 68 | return errs.Wrap(err) 69 | } 70 | for _, upload := range uploads { 71 | if err := d.deleteUpload(ctx, upload.PartHash); err != nil { 72 | return err 73 | } 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/db/user_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | "errors" 23 | 24 | "gorm.io/gorm" 25 | 26 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 27 | "github.com/openimsdk/tools/errs" 28 | ) 29 | 30 | func (d *DataBase) GetLoginUser(ctx context.Context, userID string) (*model_struct.LocalUser, error) { 31 | d.mRWMutex.RLock() 32 | defer d.mRWMutex.RUnlock() 33 | var user model_struct.LocalUser 34 | err := d.conn.WithContext(ctx).Where("user_id = ? ", userID).Take(&user).Error 35 | if err != nil { 36 | if err == gorm.ErrRecordNotFound { 37 | return nil, errs.ErrRecordNotFound.Wrap() 38 | } 39 | return nil, errs.Wrap(err) 40 | } 41 | return &user, nil 42 | } 43 | 44 | func (d *DataBase) UpdateLoginUser(ctx context.Context, user *model_struct.LocalUser) error { 45 | d.mRWMutex.Lock() 46 | defer d.mRWMutex.Unlock() 47 | t := d.conn.WithContext(ctx).Model(user).Select("*").Updates(user) 48 | if t.RowsAffected == 0 { 49 | return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update") 50 | } 51 | return errs.WrapMsg(t.Error, "UpdateLoginUser failed") 52 | } 53 | func (d *DataBase) UpdateLoginUserByMap(ctx context.Context, user *model_struct.LocalUser, args map[string]interface{}) error { 54 | d.mRWMutex.Lock() 55 | defer d.mRWMutex.Unlock() 56 | t := d.conn.WithContext(ctx).Model(&user).Updates(args) 57 | if t.RowsAffected == 0 { 58 | return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update") 59 | } 60 | return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed") 61 | } 62 | func (d *DataBase) InsertLoginUser(ctx context.Context, user *model_struct.LocalUser) error { 63 | d.mRWMutex.Lock() 64 | defer d.mRWMutex.Unlock() 65 | return errs.WrapMsg(d.conn.WithContext(ctx).Create(user).Error, "InsertLoginUser failed") 66 | } 67 | -------------------------------------------------------------------------------- /pkg/db/version_sync.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !js 16 | // +build !js 17 | 18 | package db 19 | 20 | import ( 21 | "context" 22 | "errors" 23 | 24 | "gorm.io/gorm" 25 | 26 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 27 | "github.com/openimsdk/tools/errs" 28 | ) 29 | 30 | func (d *DataBase) GetVersionSync(ctx context.Context, tableName, entityID string) (*model_struct.LocalVersionSync, error) { 31 | d.mRWMutex.RLock() 32 | defer d.mRWMutex.RUnlock() 33 | var res model_struct.LocalVersionSync 34 | err := d.conn.WithContext(ctx).Where("`table_name` = ? and `entity_id` = ?", tableName, entityID).Take(&res).Error 35 | if err != nil { 36 | if errors.Is(err, gorm.ErrRecordNotFound) { 37 | return &model_struct.LocalVersionSync{}, errs.ErrRecordNotFound.Wrap() 38 | } 39 | return nil, errs.Wrap(err) 40 | } 41 | return &res, errs.Wrap(err) 42 | } 43 | 44 | func (d *DataBase) SetVersionSync(ctx context.Context, lv *model_struct.LocalVersionSync) error { 45 | d.mRWMutex.Lock() 46 | defer d.mRWMutex.Unlock() 47 | 48 | var existing model_struct.LocalVersionSync 49 | err := d.conn.WithContext(ctx).Where("`table_name` = ? AND `entity_id` = ?", lv.Table, lv.EntityID).First(&existing).Error 50 | 51 | if err == gorm.ErrRecordNotFound { 52 | if createErr := d.conn.WithContext(ctx).Create(lv).Error; createErr != nil { 53 | return errs.Wrap(createErr) 54 | } 55 | return nil 56 | } else if err != nil { 57 | return errs.Wrap(err) 58 | } 59 | 60 | if updateErr := d.conn.WithContext(ctx).Model(&existing).Updates(lv).Error; updateErr != nil { 61 | return errs.Wrap(updateErr) 62 | } 63 | 64 | return nil 65 | } 66 | 67 | func (d *DataBase) DeleteVersionSync(ctx context.Context, tableName, entityID string) error { 68 | d.mRWMutex.Lock() 69 | defer d.mRWMutex.Unlock() 70 | localVersionSync := model_struct.LocalVersionSync{Table: tableName, EntityID: entityID} 71 | return errs.WrapMsg(d.conn.WithContext(ctx).Delete(&localVersionSync).Error, "DeleteVersionSync failed") 72 | } 73 | -------------------------------------------------------------------------------- /pkg/db/version_sync_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 8 | ) 9 | 10 | func Test_GetVersionSync(t *testing.T) { 11 | 12 | ctx := context.Background() 13 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 14 | if err != nil { 15 | return 16 | } 17 | 18 | _, err = db.GetVersionSync(ctx, "local_group_entities_version", "1076204769") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | // log.ZDebug(ctx, "info is ", "info", info) 26 | } 27 | 28 | func Test_SetVersionSync(t *testing.T) { 29 | 30 | ctx := context.Background() 31 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 32 | if err != nil { 33 | return 34 | } 35 | 36 | newVersionSync := &model_struct.LocalVersionSync{ 37 | Table: "local_group_entities_version", 38 | EntityID: "1076204769", 39 | VersionID: "667aabe3417b67f0f0d3cdee", 40 | Version: 1076204769, 41 | CreateTime: 0, 42 | UIDList: []string{"8879166186", "1695766238", "2882899447", "5292156665"}, 43 | } 44 | 45 | err = db.SetVersionSync(ctx, newVersionSync) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | // log.ZDebug(ctx, "info is ", "info", info) 53 | } 54 | 55 | func Test_DeleteVersionSync(t *testing.T) { 56 | 57 | ctx := context.Background() 58 | db, err := NewDataBase(ctx, "1695766238", "./", 6) 59 | if err != nil { 60 | return 61 | } 62 | 63 | err = db.DeleteVersionSync(ctx, "local_group_entities_version", "3378458183") 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | // log.ZDebug(ctx, "info is ", "info", info) 71 | } 72 | -------------------------------------------------------------------------------- /pkg/network/http_client_test.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "context" 5 | 6 | "testing" 7 | 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext" 9 | ) 10 | 11 | func TestName(t *testing.T) { 12 | var conf ccontext.GlobalConfig 13 | conf.ApiAddr = "http://127.0.0.1:8080" 14 | ctx := ccontext.WithInfo(context.Background(), &conf) 15 | ctx = ccontext.WithOperationID(ctx, "123456") 16 | var resp any 17 | if err := ApiPost(ctx, "/test", map[string]any{}, &resp); err != nil { 18 | t.Log(err) 19 | return 20 | } 21 | t.Log("success") 22 | } 23 | -------------------------------------------------------------------------------- /pkg/page/pagereq.go: -------------------------------------------------------------------------------- 1 | package page 2 | 3 | import "github.com/openimsdk/protocol/sdkws" 4 | 5 | type PageReq interface { 6 | GetPagination() *sdkws.RequestPagination 7 | } 8 | -------------------------------------------------------------------------------- /pkg/sdk_params_callback/friend_sdk_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sdk_params_callback 16 | 17 | import ( 18 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 19 | "github.com/openimsdk/protocol/wrapperspb" 20 | ) 21 | 22 | type ProcessFriendApplicationParams struct { 23 | ToUserID string `json:"toUserID" validate:"required"` 24 | HandleMsg string `json:"handleMsg"` 25 | } 26 | 27 | type SearchFriendsParam struct { 28 | KeywordList []string `json:"keywordList"` 29 | IsSearchUserID bool `json:"isSearchUserID"` 30 | IsSearchNickname bool `json:"isSearchNickname"` 31 | IsSearchRemark bool `json:"isSearchRemark"` 32 | } 33 | 34 | type SearchFriendItem struct { 35 | model_struct.LocalFriend 36 | Relationship int `json:"relationship"` 37 | } 38 | 39 | type SetFriendRemarkParams struct { 40 | ToUserID string `json:"toUserID" validate:"required"` 41 | Remark string `json:"remark" validate:"required"` 42 | } 43 | type SetFriendPinParams struct { 44 | ToUserIDs []string `json:"toUserIDs" validate:"required"` 45 | IsPinned *wrapperspb.BoolValue `json:"isPinned" validate:"required"` 46 | } 47 | -------------------------------------------------------------------------------- /pkg/sdk_params_callback/group_sdk_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sdk_params_callback 16 | 17 | type SearchGroupsParam struct { 18 | KeywordList []string `json:"keywordList"` 19 | IsSearchGroupID bool `json:"isSearchGroupID"` 20 | IsSearchGroupName bool `json:"isSearchGroupName"` 21 | } 22 | 23 | type SearchGroupMembersParam struct { 24 | GroupID string `json:"groupID"` 25 | KeywordList []string `json:"keywordList"` 26 | IsSearchUserID bool `json:"isSearchUserID"` 27 | IsSearchMemberNickname bool `json:"isSearchMemberNickname"` 28 | Offset int `json:"offset"` 29 | Count int `json:"count"` 30 | PageNumber int `json:"pageNumber"` 31 | } 32 | -------------------------------------------------------------------------------- /pkg/sdkerrs/code.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sdkerrs 16 | 17 | // Common error codes 18 | const ( 19 | NetworkError = 10000 20 | NetworkTimeoutError = 10001 21 | ArgsError = 10002 // Invalid input parameters 22 | CtxDeadlineExceededError = 10003 // Context deadline exceeded 23 | 24 | UnknownCode = 10005 // Unrecognized code 25 | SdkInternalError = 10006 // SDK internal error 26 | 27 | NoUpdateError = 10007 // No updates available 28 | SDKNotInitError = 10008 // SDK not init 29 | SDKNotLoginError = 10009 // SDK not login 30 | 31 | UserIDNotFoundError = 10100 // UserID not found or not registered 32 | LoginOutError = 10101 // User has logged out 33 | LoginRepeatError = 10102 // User logged in repeatedly 34 | 35 | // Message-related errors 36 | FileNotFoundError = 10200 // Record not found 37 | MsgDeCompressionError = 10201 // Message decompression failed 38 | MsgDecodeBinaryWsError = 10202 // Message decoding failed 39 | MsgBinaryTypeNotSupportError = 10203 // Message type not supported 40 | MsgRepeatError = 10204 // Message repeated 41 | MsgContentTypeNotSupportError = 10205 // Message content type not supported 42 | MsgHasNoSeqError = 10206 // Message does not have a sequence number 43 | MsgHasDeletedError = 10207 // Message has been deleted 44 | 45 | // Conversation-related errors 46 | NotSupportOptError = 10301 // Operation not supported 47 | NotSupportTypeError = 10302 // Type not supported 48 | UnreadCountError = 10303 // Unread count is zero 49 | 50 | // Group-related errors 51 | GroupIDNotFoundError = 10400 // GroupID not found 52 | GroupTypeErr = 10401 // Invalid group type 53 | ) 54 | -------------------------------------------------------------------------------- /pkg/sdkerrs/error.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sdkerrs 16 | 17 | import "github.com/openimsdk/tools/errs" 18 | 19 | func New(code int, msg string, dtl string) errs.CodeError { 20 | return errs.NewCodeError(code, msg).WithDetail(dtl) 21 | } 22 | 23 | var ( 24 | Wrap = errs.Wrap 25 | WrapMsg = errs.WrapMsg 26 | ) 27 | -------------------------------------------------------------------------------- /pkg/server_api_params/conversation_api_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server_api_params 16 | 17 | type Conversation struct { 18 | OwnerUserID string `json:"ownerUserID" binding:"required"` 19 | ConversationID string `json:"conversationID" binding:"required"` 20 | ConversationType int32 `json:"conversationType" binding:"required"` 21 | UserID string `json:"userID"` 22 | GroupID string `json:"groupID"` 23 | RecvMsgOpt int32 `json:"recvMsgOpt"` 24 | UnreadCount int32 `json:"unreadCount"` 25 | DraftTextTime int64 `json:"draftTextTime"` 26 | IsPinned bool `json:"isPinned"` 27 | IsPrivateChat bool `json:"isPrivateChat"` 28 | BurnDuration int32 `json:"burnDuration"` 29 | GroupAtType int32 `json:"groupAtType"` 30 | IsNotInGroup bool `json:"isNotInGroup"` 31 | UpdateUnreadCountTime int64 ` json:"updateUnreadCountTime"` 32 | AttachedInfo string `json:"attachedInfo"` 33 | Ex string `json:"ex"` 34 | } 35 | -------------------------------------------------------------------------------- /pkg/server_api_params/friend_api_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server_api_params 16 | 17 | type UserIDResult struct { 18 | UserID string `json:"userID"` 19 | Result int32 `json:"result"` 20 | } 21 | -------------------------------------------------------------------------------- /pkg/syncer/state.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package syncer 16 | 17 | const ( 18 | Unchanged = 0 19 | Insert = 1 20 | Update = 2 21 | Delete = 3 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/utils/file.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "io" 19 | "os" 20 | "path" 21 | ) 22 | 23 | func CopyFile(srcName string, dstName string) (written int64, err error) { 24 | src, err := os.Open(srcName) 25 | if err != nil { 26 | return 27 | } 28 | dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644) 29 | if err != nil { 30 | return 31 | } 32 | 33 | defer func() { 34 | if src != nil { 35 | src.Close() 36 | } 37 | if dst != nil { 38 | dst.Close() 39 | } 40 | }() 41 | return io.Copy(dst, src) 42 | } 43 | 44 | func FileTmpPath(fullPath, dbPrefix string) string { 45 | suffix := path.Ext(fullPath) 46 | return dbPrefix + Md5(fullPath) + suffix //a->b 47 | } 48 | 49 | func FileExist(filename string) bool { 50 | _, err := os.Stat(filename) 51 | return err == nil || os.IsExist(err) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/utils/lock_pool.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sync" 4 | 5 | // resource manager 6 | type LockPool struct { 7 | maxLocks int 8 | locks map[string]struct{} 9 | 10 | globalMutex sync.Mutex 11 | cond *sync.Cond 12 | } 13 | 14 | // Usage 15 | 16 | func NewLockPool(maxLocks int) *LockPool { 17 | lp := &LockPool{ 18 | maxLocks: maxLocks, 19 | locks: make(map[string]struct{}), 20 | } 21 | 22 | lp.cond = sync.NewCond(&lp.globalMutex) 23 | return lp 24 | } 25 | 26 | func (lp *LockPool) Lock(key string) { 27 | lp.globalMutex.Lock() 28 | defer lp.globalMutex.Unlock() 29 | 30 | for { 31 | // If the key exists in the map, then wait. 32 | if _, exists := lp.locks[key]; exists { 33 | lp.cond.Wait() 34 | } else if len(lp.locks) >= lp.maxLocks { 35 | // If the number of locks reaches the maximum value, then wait. 36 | lp.cond.Wait() 37 | } else { 38 | lp.locks[key] = struct{}{} 39 | return 40 | } 41 | } 42 | } 43 | 44 | func (lp *LockPool) Unlock(key string) { 45 | lp.globalMutex.Lock() 46 | defer lp.globalMutex.Unlock() 47 | 48 | delete(lp.locks, key) 49 | // wakes all goroutines waiting 50 | lp.cond.Broadcast() 51 | } 52 | -------------------------------------------------------------------------------- /pkg/version/README.md: -------------------------------------------------------------------------------- 1 | # OpenIM SDK Version Management 2 | 3 | OpenIM SDK uses a unique approach to version management by integrating principles from the Kubernetes project version management design. 4 | 5 | ## Versioning Methodology: 6 | 7 | Whenever you run the server CTL via the `go build` command, the system will automatically obtain the current `git commit hash`. This commit hash is then set as the version number and written to the `pkg/version/version.go` file. 8 | 9 | This approach ensures that each build is distinct and can be traced back to the specific commit hash from which it was built. It is a transparent and easy way to track changes and modifications across different versions of the SDK. 10 | 11 | ## How to Check Version: 12 | 13 | - **Server Version**: Use the command `imctl version`. 14 | - **Client Version**: Execute the command `go run main.go version`. 15 | 16 | ## SDK Versioning: 17 | 18 | 1. **Traditional SDK Versioning**: The standard method of versioning that uses Major, Minor, and Patch numbers. 19 | 2. **Client-Represented Versioning**: This is primarily managed through the Go versioning system. It helps in understanding the compatibility and dependencies. 20 | 21 | ## Code for Version Management: 22 | 23 | ### Utility to Compare Version Strings: 24 | 25 | This function compares two version strings that follow the OpenIM versioning pattern. 26 | 27 | ``` 28 | goCopy codefunc CompareOpenIMAwareVersionStrings(v1, v2 string) int { 29 | ... 30 | } 31 | ``` 32 | 33 | ### Information Structure for Version: 34 | 35 | This struct contains various version-related information, making it easy to understand and process the build details. 36 | 37 | ``` 38 | goCopy codetype Info struct { 39 | ... 40 | } 41 | 42 | func (info Info) String() string { 43 | ... 44 | } 45 | 46 | func Get() Info { 47 | ... 48 | } 49 | ``` 50 | 51 | ## Conclusion: 52 | 53 | Effective version management is crucial for the growth and stability of any SDK. With OpenIM SDK's approach, users can be confident about the build's origin and its compatibility. The code snippets provided above can be utilized to integrate this versioning mechanism into any project seamlessly. 54 | 55 | ------ 56 | 57 | Hope this helps! -------------------------------------------------------------------------------- /pkg/version/base.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Base version information. 4 | // 5 | // This is the fallback data used when version information from git is not 6 | // provided via go ldflags. It provides an approximation of the OpenIM 7 | // version for ad-hoc builds (e.g. `go build`) that cannot get the version 8 | // information from git. 9 | // 10 | // If you are looking at these fields in the git tree, they look 11 | // strange. They are modified on the fly by the build process. The 12 | // in-tree values are dummy values used for "git archive", which also 13 | // works for GitHub tar downloads. 14 | // 15 | // When releasing a new OpenIM version, this file is updated by 16 | // build/mark_new_version.sh to reflect the new version, and then a 17 | // git annotated tag (using format vX.Y where X == Major version and Y 18 | // == Minor version) is created to point to the commit that updates 19 | // pkg/version/base.go. 20 | var ( 21 | // TODO: Deprecate gitMajor and gitMinor, use only gitVersion 22 | // instead. First step in deprecation, keep the fields but make 23 | // them irrelevant. (Next we'll take it out, which may muck with 24 | // scripts consuming the imctl version output - but most of 25 | // these should be looking at gitVersion already anyways.) 26 | gitMajor string = "" // major version, always numeric 27 | gitMinor string = "" // minor version, numeric possibly followed by "+" 28 | 29 | // semantic version, derived by build scripts (see 30 | // https://git.k8s.io/community/contributors/design-proposals/release/versioning.md 31 | // for a detailed discussion of this field) 32 | // 33 | // TODO: This field is still called "gitVersion" for legacy 34 | // reasons. For prerelease versions, the build metadata on the 35 | // semantic version is a git hash, but the version itself is no 36 | // longer the direct output of "git describe", but a slight 37 | // translation to be semver compliant. 38 | 39 | // NOTE: The $Format strings are replaced during 'git archive' thanks to the 40 | // companion .gitattributes file containing 'export-subst' in this same 41 | // directory. See also https://git-scm.com/docs/gitattributes 42 | gitVersion string = "v0.0.0-main+$Format:%h$" 43 | gitCommit string = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD) 44 | gitTreeState string = "" // state of git tree, either "clean" or "dirty" 45 | 46 | buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') 47 | ) 48 | -------------------------------------------------------------------------------- /pkg/version/helpers.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type versionType int 10 | 11 | const ( 12 | // Bigger the version type number, higher priority it is 13 | versionTypeAlpha versionType = iota 14 | versionTypeBeta 15 | versionTypeGA 16 | ) 17 | 18 | var OpenIMVersionRegex = regexp.MustCompile("^v([\\d]+)(?:(alpha|beta)([\\d]+))?$") 19 | 20 | func parseOpenIMVersion(v string) (majorVersion int, vType versionType, minorVersion int, ok bool) { 21 | var err error 22 | submatches := OpenIMVersionRegex.FindStringSubmatch(v) 23 | if len(submatches) != 4 { 24 | return 0, 0, 0, false 25 | } 26 | switch submatches[2] { 27 | case "alpha": 28 | vType = versionTypeAlpha 29 | case "beta": 30 | vType = versionTypeBeta 31 | case "": 32 | vType = versionTypeGA 33 | default: 34 | return 0, 0, 0, false 35 | } 36 | if majorVersion, err = strconv.Atoi(submatches[1]); err != nil { 37 | return 0, 0, 0, false 38 | } 39 | if vType != versionTypeGA { 40 | if minorVersion, err = strconv.Atoi(submatches[3]); err != nil { 41 | return 0, 0, 0, false 42 | } 43 | } 44 | return majorVersion, vType, minorVersion, true 45 | } 46 | 47 | // CompareOpenIMAwareVersionStrings compares two OpenIM-like version strings. 48 | // OpenIM-like version strings are starting with a v, followed by a major version, optional "alpha" or "beta" strings 49 | // followed by a minor version (e.g. v1, v2beta1). Versions will be sorted based on GA/alpha/beta first and then major 50 | // and minor versions. e.g. v2, v1, v1beta2, v1beta1, v1alpha1. 51 | func CompareOpenIMAwareVersionStrings(v1, v2 string) int { 52 | if v1 == v2 { 53 | return 0 54 | } 55 | v1major, v1type, v1minor, ok1 := parseOpenIMVersion(v1) 56 | v2major, v2type, v2minor, ok2 := parseOpenIMVersion(v2) 57 | switch { 58 | case !ok1 && !ok2: 59 | return strings.Compare(v2, v1) 60 | case !ok1 && ok2: 61 | return -1 62 | case ok1 && !ok2: 63 | return 1 64 | } 65 | if v1type != v2type { 66 | return int(v1type) - int(v2type) 67 | } 68 | if v1major != v2major { 69 | return v1major - v2major 70 | } 71 | return v1minor - v2minor 72 | } 73 | -------------------------------------------------------------------------------- /pkg/version/types.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Info contains versioning information. 4 | // TODO: Add []string of api versions supported? It's still unclear 5 | // how we'll want to distribute that information. 6 | type Info struct { 7 | Major string `json:"major"` 8 | Minor string `json:"minor"` 9 | GitVersion string `json:"gitVersion"` 10 | GitCommit string `json:"gitCommit"` 11 | GitTreeState string `json:"gitTreeState"` 12 | BuildDate string `json:"buildDate"` 13 | GoVersion string `json:"goVersion"` 14 | Compiler string `json:"compiler"` 15 | Platform string `json:"platform"` 16 | } 17 | 18 | // String returns info as a human-friendly version string. 19 | func (info Info) String() string { 20 | return info.GitVersion 21 | } 22 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | // Get returns the overall codebase version. It's for detecting 9 | // what code a binary was built from. 10 | func Get() Info { 11 | // These variables typically come from -ldflags settings and in 12 | // their absence fallback to the settings in pkg/version/base.go 13 | return Info{ 14 | Major: gitMajor, 15 | Minor: gitMinor, 16 | GitVersion: gitVersion, 17 | GitCommit: gitCommit, 18 | GitTreeState: gitTreeState, 19 | BuildDate: buildDate, 20 | GoVersion: runtime.Version(), 21 | Compiler: runtime.Compiler, 22 | Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /scripts/template/footer.md.tmpl: -------------------------------------------------------------------------------- 1 | **Full Changelog**: https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }} 2 | 3 | ## Get Involved with OpenIM! 4 | 5 | Your patronage towards OpenIM is greatly appreciated 🎉🎉. 6 | 7 | If you encounter any problems during its usage, please create an issue in the [GitHub repository](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/), we're committed to resolving your problem as soon as possible. 8 | 9 | **Here are some ways to get involved with the OpenIM community:** 10 | 11 | 📢 **Slack Channel**: Join our Slack channels for discussions, communication, and support. Click [here](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) to join the Open-IM-Server Slack team channel. 12 | 13 | 📧 **Gmail Contact**: If you have any questions, suggestions, or feedback for our open-source projects, please feel free to [contact us via email](https://mail.google.com/mail/?view=cm&fs=1&tf=1&to=winxu81@gmail.com). 14 | 15 | 📖 **Blog**: Stay up-to-date with OpenIM-Server projects and trends by reading our [blog](https://doc.rentsoft.cn/). We share the latest developments, tech trends, and other interesting information related to OpenIM. 16 | 17 | 📱 **WeChat**: Add us on WeChat (QR Code) and indicate that you are a user or developer of Open-IM-Server. We'll process your request as soon as possible. 18 | 19 | Remember, your contributions play a vital role in making OpenIM successful, and we look forward to your active participation in our community! 🙌 -------------------------------------------------------------------------------- /scripts/template/head.md.tmpl: -------------------------------------------------------------------------------- 1 | ## Welcome to the {{ .Tag }} release of [OpenIM](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }})!🎉🎉! 2 | 3 | We are excited to release {{.Tag}}, Branch: https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/tree/{{ .Tag }} , Git hash [{{ .ShortCommit }}], Install Address: [{{ .ReleaseURL }}]({{ .ReleaseURL }}) 4 | 5 | Learn more about versions of OpenIM: 6 | 7 | + For information on versions of OpenIM and how to maintain branches, read [📚this article](https://github.com/openimsdk/Open-IM-Server/blob/main/docs/conversions/version.md) 8 | 9 | + If you wish to use mirroring, read OpenIM's [🤲image management policy](https://github.com/openimsdk/Open-IM-Server/blob/main/docs/conversions/images.md) 10 | 11 | **Want to be one of them 😘?** 12 | 13 |

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

24 | 25 | > **Note** 26 | > @openimbot and @kubbot have made great contributions to the community as community 🤖robots(@openimsdk/bot), respectively. 27 | > Thanks to the @openimsdk/openim team for all their hard work on this release. 28 | > Thank you to all the [💕developers and contributors](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/graphs/contributors), people from all over the world, OpenIM brings us together 29 | -------------------------------------------------------------------------------- /test/callback.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import "fmt" 18 | 19 | type TestSendMsg struct { 20 | } 21 | 22 | func (TestSendMsg) OnSuccess(data string) { 23 | fmt.Println("testSendImg, OnSuccess, output: ", data) 24 | } 25 | 26 | func (TestSendMsg) OnError(code int32, msg string) { 27 | fmt.Println("testSendImg, OnError, ", code, msg) 28 | } 29 | 30 | func (TestSendMsg) OnProgress(progress int) { 31 | fmt.Println("progress: ", progress) 32 | } 33 | -------------------------------------------------------------------------------- /test/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import "github.com/openimsdk/protocol/constant" 18 | 19 | const ( 20 | APIADDR = "http://127.0.0.1:10002" 21 | WSADDR = "ws://127.0.0.1:10001" 22 | UserID = "3691628604" 23 | ) 24 | 25 | const ( 26 | PlatformID = constant.LinuxPlatformID 27 | Secret = "openIM123" 28 | AdminUserID = "imAdmin" 29 | ) 30 | -------------------------------------------------------------------------------- /test/empty_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 22 | "github.com/openimsdk/tools/log" 23 | ) 24 | 25 | func TestName(t *testing.T) { 26 | res, err := open_im_sdk.IMUserContext.Conversation().GetActiveConversations(ctx) 27 | if err != nil { 28 | t.Error("GetActiveConversations", err) 29 | return 30 | } 31 | t.Log("GetActiveConversations", res) 32 | } 33 | 34 | func Test_Empty(t *testing.T) { 35 | for { 36 | time.Sleep(time.Second * 1) 37 | } 38 | } 39 | 40 | func Test_ChangeInputState(t *testing.T) { 41 | for { 42 | err := open_im_sdk.IMUserContext.Conversation().ChangeInputStates(ctx, "sg_2309860938", true) 43 | if err != nil { 44 | log.ZError(ctx, "ChangeInputStates", err) 45 | } 46 | time.Sleep(time.Second * 1) 47 | } 48 | } 49 | 50 | func Test_RunWait(t *testing.T) { 51 | time.Sleep(time.Second * 10) 52 | } 53 | 54 | func Test_OnlineState(t *testing.T) { 55 | defer func() { select {} }() 56 | userIDs := []string{ 57 | //"3611802798", 58 | "2110910952", 59 | } 60 | for i := 1; ; i++ { 61 | time.Sleep(time.Second) 62 | //open_im_sdk.IMUserContext.LongConnMgr().UnsubscribeUserOnlinePlatformIDs(ctx, userIDs) 63 | res, err := open_im_sdk.IMUserContext.LongConnMgr().GetUserOnlinePlatformIDs(ctx, userIDs) 64 | if err != nil { 65 | t.Logf("@@@@@@@@@@@@=====> <%d> error %s", i, err) 66 | continue 67 | } 68 | t.Logf("@@@@@@@@@@@@=====> <%d> success %+v", i, res) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/openimsdk/openim-sdk-core/v3/internal/third/file" 22 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 23 | ) 24 | 25 | func TestUploadFile(t *testing.T) { 26 | 27 | fp := `C:\Users\openIM\Desktop\dist.zip` 28 | 29 | resp, err := open_im_sdk.IMUserContext.File().UploadFile(ctx, &file.UploadFileReq{ 30 | Filepath: fp, 31 | Name: filepath.Base(fp), 32 | Cause: "test", 33 | }, nil) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | t.Log(resp) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /test/long_conn_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 8 | ) 9 | 10 | func Test_SubscribeUsersStatus(t *testing.T) { 11 | time.Sleep(time.Second) 12 | message, err := open_im_sdk.IMUserContext.LongConnMgr().SubscribeUsersStatus(ctx, []string{"5975996883"}) 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | t.Log(message) 17 | ch := make(chan struct{}) 18 | select { 19 | case <-ch: 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/third_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 9 | ) 10 | 11 | type SProgress struct{} 12 | 13 | func (s SProgress) OnProgress(current int64, size int64) { 14 | 15 | } 16 | 17 | func Test_UploadLog(t *testing.T) { 18 | tm := time.Now() 19 | err := open_im_sdk.IMUserContext.Third().UploadLogs(ctx, 2000, "it is ex", SProgress{}) 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | fmt.Println(time.Since(tm).Microseconds()) 24 | 25 | } 26 | func Test_SDKLogs(t *testing.T) { 27 | open_im_sdk.IMUserContext.Third().Log(ctx, 4, "cmd/abc.go", 666, "This is a test message", "", []any{"key", "value"}) 28 | } 29 | -------------------------------------------------------------------------------- /test/user_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/openimsdk/protocol/wrapperspb" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 24 | 25 | "github.com/openimsdk/protocol/sdkws" 26 | ) 27 | 28 | func Test_GetSelfUserInfo(t *testing.T) { 29 | userInfo, err := open_im_sdk.IMUserContext.User().GetSelfUserInfo(ctx) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | 34 | t.Log(userInfo) 35 | } 36 | 37 | func Test_SetSelfInfoEx(t *testing.T) { 38 | newNickName := "test" 39 | //newFaceURL := "http://test.com" 40 | err := open_im_sdk.IMUserContext.User().SetSelfInfo(ctx, &sdkws.UserInfoWithEx{ 41 | Nickname: &wrapperspb.StringValue{ 42 | Value: newNickName, 43 | }, 44 | //FaceURL: newFaceURL, 45 | Ex: &wrapperspb.StringValue{ 46 | Value: "ASD", 47 | }, 48 | }) 49 | newFaceURL := "http://test.com" 50 | 51 | if err != nil { 52 | t.Error(err) 53 | } 54 | userInfo, err := open_im_sdk.IMUserContext.User().GetSelfUserInfo(ctx) 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | if userInfo.UserID != UserID && userInfo.Nickname != newNickName && userInfo.FaceURL != newFaceURL { 59 | t.Error("user id not match") 60 | } 61 | t.Log(userInfo) 62 | time.Sleep(time.Second * 10) 63 | } 64 | -------------------------------------------------------------------------------- /version/version: -------------------------------------------------------------------------------- 1 | 3.8.0 -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | _ "embed" 5 | "strings" 6 | ) 7 | 8 | //go:embed version 9 | var Version string 10 | 11 | func init() { 12 | Version = strings.Trim(Version, "\n") 13 | Version = strings.TrimSpace(Version) 14 | } 15 | -------------------------------------------------------------------------------- /wasm/cmd/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: ios build install mac windows 2 | 3 | WASM_BIN_NAME=openIM.wasm 4 | 5 | 6 | # When adding a new target, remember to align the following command line with a tab at the beginning for proper indentation. 7 | wasm: 8 | GOOS=js GOARCH=wasm go build -trimpath -ldflags "-s -w" -o ${WASM_BIN_NAME} main.go 9 | 10 | build: static/main.wasm static/wasm_exec.js 11 | goexec 'http.ListenAndServe(`:9999`, http.FileServer(http.Dir(`.`)))' 12 | 13 | static/wasm_exec.js: 14 | cp "$(shell go env GOROOT)/misc/wasm/wasm_exec.js" static 15 | 16 | static/main.wasm : main.go 17 | GO111MODULE=auto GOOS=js GOARCH=wasm go build -o static/${WASM_BIN_NAME}.wasm main.go -------------------------------------------------------------------------------- /wasm/indexdb/app_version.go: -------------------------------------------------------------------------------- 1 | //go:build js && wasm 2 | // +build js,wasm 3 | 4 | package indexdb 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 10 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 11 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 12 | ) 13 | 14 | type LocalAppSDKVersion struct { 15 | } 16 | 17 | func NewLocalAppSDKVersion() *LocalAppSDKVersion { 18 | return &LocalAppSDKVersion{} 19 | } 20 | 21 | func (i *LocalAppSDKVersion) GetAppSDKVersion(ctx context.Context) (*model_struct.LocalAppSDKVersion, error) { 22 | sdkVersion, err := exec.Exec() 23 | if err != nil { 24 | return nil, err 25 | } else { 26 | if v, ok := sdkVersion.(string); ok { 27 | var temp model_struct.LocalAppSDKVersion 28 | if err := utils.JsonStringToStruct(v, &temp); err != nil { 29 | return nil, err 30 | } 31 | return &temp, err 32 | } else { 33 | return nil, exec.ErrType 34 | } 35 | } 36 | } 37 | 38 | func (i *LocalAppSDKVersion) SetAppSDKVersion(ctx context.Context, appVersion *model_struct.LocalAppSDKVersion) error { 39 | _, err := exec.Exec(utils.StructToJsonString(appVersion)) 40 | return err 41 | } 42 | -------------------------------------------------------------------------------- /wasm/indexdb/conversation_unread_message_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build js && wasm 16 | // +build js,wasm 17 | 18 | package indexdb 19 | 20 | import ( 21 | "context" 22 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 23 | ) 24 | 25 | import ( 26 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 27 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 28 | ) 29 | 30 | type LocalConversationUnreadMessages struct { 31 | } 32 | 33 | func NewLocalConversationUnreadMessages() *LocalConversationUnreadMessages { 34 | return &LocalConversationUnreadMessages{} 35 | } 36 | 37 | func (i *LocalConversationUnreadMessages) BatchInsertConversationUnreadMessageList(ctx context.Context, messageList []*model_struct.LocalConversationUnreadMessage) error { 38 | if messageList == nil { 39 | return nil 40 | } 41 | _, err := exec.Exec(utils.StructToJsonString(messageList)) 42 | return err 43 | } 44 | 45 | func (i *LocalConversationUnreadMessages) DeleteConversationUnreadMessageList(ctx context.Context, conversationID string, sendTime int64) int64 { 46 | deleteRows, err := exec.Exec(conversationID, sendTime) 47 | if err != nil { 48 | return 0 49 | } else { 50 | if v, ok := deleteRows.(float64); ok { 51 | var result int64 52 | result = int64(v) 53 | return result 54 | } else { 55 | return 0 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /wasm/indexdb/init.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build js && wasm 16 | // +build js,wasm 17 | 18 | package indexdb 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 24 | ) 25 | 26 | // 1. Using the native wasm method, TinyGo is applied to Go's embedded domain, 27 | // but the supported functionality is limited and only supports a subset of Go. 28 | // Even JSON serialization is not supported. 29 | // 2. Function names should follow camelCase convention. 30 | // 3. In the provided SQL generation statements, boolean values need special handling. 31 | // For create statements, boolean values should be explicitly handled because SQLite does not natively support boolean types. 32 | // Instead, integers (1 or 0) are used as substitutes, and GORM converts them automatically. 33 | // 4. In the provided SQL generation statements, field names use snake_case (e.g., recv_id), 34 | // but in the interface-converted data, the JSON tag fields follow camelCase (e.g., recvID). 35 | // All such fields should have a mapped transformation. 36 | // 5. Any GORM operations that involve retrieval (e.g., take and find) should specify whether they need to return an error in the documentation. 37 | // 6. For any update operations, be sure to check GORM's implementation. If there is a select(*) query involved, 38 | // you do not need to use the structures in temp_struct. 39 | // 7. Whenever there's a name conflict with an interface, the DB interface should append the "DB" suffix. 40 | // 8. For any map types, use JSON string conversion, and document this clearly. 41 | 42 | type IndexDB struct { 43 | LocalUsers 44 | LocalConversations 45 | *LocalChatLogs 46 | LocalConversationUnreadMessages 47 | LocalGroups 48 | LocalGroupMember 49 | LocalGroupRequest 50 | *FriendRequest 51 | *Black 52 | *Friend 53 | loginUserID string 54 | } 55 | 56 | func (i IndexDB) Close(ctx context.Context) error { 57 | _, err := exec.Exec() 58 | return err 59 | } 60 | 61 | func (i IndexDB) InitDB(ctx context.Context, userID string, dataDir string) error { 62 | _, err := exec.Exec(userID, dataDir) 63 | return err 64 | } 65 | 66 | func (i IndexDB) SetChatLogFailedStatus(ctx context.Context) { 67 | } 68 | -------------------------------------------------------------------------------- /wasm/indexdb/notification_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build js && wasm 16 | // +build js,wasm 17 | 18 | package indexdb 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 24 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 25 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 26 | ) 27 | 28 | type NotificationSeqs struct { 29 | } 30 | 31 | func NewNotificationSeqs() *NotificationSeqs { 32 | return &NotificationSeqs{} 33 | } 34 | 35 | func (i *NotificationSeqs) SetNotificationSeq(ctx context.Context, conversationID string, seq int64) error { 36 | _, err := exec.Exec(conversationID, seq) 37 | return err 38 | } 39 | 40 | func (i *NotificationSeqs) BatchInsertNotificationSeq(ctx context.Context, notificationSeqs []*model_struct.NotificationSeqs) error { 41 | _, err := exec.Exec(utils.StructToJsonString(notificationSeqs)) 42 | return err 43 | } 44 | 45 | func (i *NotificationSeqs) GetNotificationAllSeqs(ctx context.Context) (result []*model_struct.NotificationSeqs, err error) { 46 | gList, err := exec.Exec() 47 | if err != nil { 48 | return nil, err 49 | } else { 50 | if v, ok := gList.(string); ok { 51 | err := utils.JsonStringToStruct(v, &result) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return result, err 56 | } else { 57 | return nil, exec.ErrType 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /wasm/indexdb/sending_messages_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build js && wasm 16 | // +build js,wasm 17 | 18 | package indexdb 19 | 20 | import ( 21 | "context" 22 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 24 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 25 | ) 26 | 27 | type LocalSendingMessages struct { 28 | } 29 | 30 | func NewLocalSendingMessages() *LocalSendingMessages { 31 | return &LocalSendingMessages{} 32 | } 33 | func (i *LocalSendingMessages) InsertSendingMessage(ctx context.Context, message *model_struct.LocalSendingMessages) error { 34 | _, err := exec.Exec(utils.StructToJsonString(message)) 35 | return err 36 | } 37 | 38 | func (i *LocalSendingMessages) DeleteSendingMessage(ctx context.Context, conversationID, clientMsgID string) error { 39 | _, err := exec.Exec(conversationID, clientMsgID) 40 | return err 41 | } 42 | func (i *LocalSendingMessages) GetAllSendingMessages(ctx context.Context) (result []*model_struct.LocalSendingMessages, err error) { 43 | gList, err := exec.Exec() 44 | if err != nil { 45 | return nil, err 46 | } else { 47 | if v, ok := gList.(string); ok { 48 | var temp []model_struct.LocalSendingMessages 49 | err := utils.JsonStringToStruct(v, &temp) 50 | if err != nil { 51 | return nil, err 52 | } 53 | for _, v := range temp { 54 | v1 := v 55 | result = append(result, &v1) 56 | } 57 | return result, err 58 | } else { 59 | return nil, exec.ErrType 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /wasm/indexdb/table_master.go: -------------------------------------------------------------------------------- 1 | //go:build js && wasm 2 | // +build js,wasm 3 | 4 | package indexdb 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 10 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 11 | ) 12 | 13 | type LocalTableMaster struct { 14 | } 15 | 16 | func NewLocalTableMaster() *LocalTableMaster { 17 | return &LocalTableMaster{} 18 | } 19 | 20 | func (i *LocalTableMaster) GetExistTables(ctx context.Context) (result []string, err error) { 21 | nameList, err := exec.Exec() 22 | if err != nil { 23 | return nil, err 24 | } else { 25 | if v, ok := nameList.(string); ok { 26 | err := utils.JsonStringToStruct(v, &result) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return result, err 31 | } else { 32 | return nil, exec.ErrType 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wasm/indexdb/upload_model.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM SDK. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build js && wasm 16 | // +build js,wasm 17 | 18 | package indexdb 19 | 20 | import ( 21 | "context" 22 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 23 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 24 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 25 | ) 26 | 27 | type LocalUpload struct{} 28 | 29 | func NewLocalUpload() *LocalUpload { 30 | return &LocalUpload{} 31 | } 32 | 33 | func (i *LocalUpload) GetUpload(ctx context.Context, partHash string) (*model_struct.LocalUpload, error) { 34 | c, err := exec.Exec(partHash) 35 | if err != nil { 36 | return nil, err 37 | } else { 38 | if v, ok := c.(string); ok { 39 | result := model_struct.LocalUpload{} 40 | err := utils.JsonStringToStruct(v, &result) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return &result, err 45 | } else { 46 | return nil, exec.ErrType 47 | } 48 | } 49 | } 50 | 51 | func (i *LocalUpload) InsertUpload(ctx context.Context, upload *model_struct.LocalUpload) error { 52 | _, err := exec.Exec(utils.StructToJsonString(upload)) 53 | return err 54 | } 55 | 56 | func (i *LocalUpload) DeleteUpload(ctx context.Context, partHash string) error { 57 | _, err := exec.Exec(partHash) 58 | return err 59 | } 60 | func (i *LocalUpload) UpdateUpload(ctx context.Context, upload *model_struct.LocalUpload) error { 61 | _, err := exec.Exec(utils.StructToJsonString(upload)) 62 | return err 63 | } 64 | 65 | func (i *LocalUpload) DeleteExpireUpload(ctx context.Context) error { 66 | //TODO implement me 67 | panic("implement me") 68 | } 69 | -------------------------------------------------------------------------------- /wasm/indexdb/version_sync.go: -------------------------------------------------------------------------------- 1 | //go:build js && wasm 2 | // +build js,wasm 3 | 4 | package indexdb 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct" 10 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 11 | "github.com/openimsdk/openim-sdk-core/v3/wasm/exec" 12 | "github.com/openimsdk/tools/errs" 13 | ) 14 | 15 | type LocalVersionSync struct { 16 | } 17 | 18 | func NewLocalVersionSync() *LocalVersionSync { 19 | return &LocalVersionSync{} 20 | } 21 | 22 | func (i *LocalVersionSync) GetVersionSync(ctx context.Context, tableName, entityID string) (*model_struct.LocalVersionSync, error) { 23 | version, err := exec.Exec(tableName, entityID) 24 | if err != nil { 25 | if err == errs.ErrRecordNotFound { 26 | return &model_struct.LocalVersionSync{}, err 27 | } 28 | return nil, err 29 | } 30 | if v, ok := version.(string); ok { 31 | var temp model_struct.LocalVersionSync 32 | if err := utils.JsonStringToStruct(v, &temp); err != nil { 33 | return nil, err 34 | } 35 | return &temp, err 36 | } else { 37 | return nil, exec.ErrType 38 | } 39 | } 40 | 41 | func (i *LocalVersionSync) SetVersionSync(ctx context.Context, lv *model_struct.LocalVersionSync) error { 42 | _, err := exec.Exec(utils.StructToJsonString(lv)) 43 | return err 44 | } 45 | 46 | func (i *LocalVersionSync) DeleteVersionSync(ctx context.Context, tableName, entityID string) error { 47 | _, err := exec.Exec(tableName, entityID) 48 | return err 49 | } 50 | --------------------------------------------------------------------------------