diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml
new file mode 100644
index 000000000..afc57eed9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.yaml
@@ -0,0 +1,82 @@
+name: Bug Report
+title: "[BUG] "
+labels: ["bug"]
+description: "Create a detailed report to help us identify and resolve issues."
+# assignees: []
+
+body:
+ - type: markdown
+ attributes:
+ value: "Thank you for taking the time to fill out the bug report. Please provide as much information as possible to help us understand and replicate the bug."
+
+ - type: input
+ id: openim-sdk-version
+ attributes:
+ label: OpenIM SDK Version
+ description: "Please provide the version number of OpenIM SDK you are using."
+ placeholder: "e.g., 3.8.0"
+ validations:
+ required: true
+
+ - type: input
+ id: openim-server-version
+ attributes:
+ label: OpenIM Server Version
+ description: "Please provide the version number of OpenIM Server you are using."
+ placeholder: "e.g., 3.8.0"
+
+ - type: dropdown
+ id: sdk-platform
+ attributes:
+ label: SDK Platform
+ description: "Please specify the SDK platform you are using."
+ options:
+ - iOS SDK
+ - Android SDK
+ - Web WASM SDK
+ - Electron FFI SDK
+ - Flutter SDK
+ - uni-app SDK
+ - Unity SDK
+ - .NET SDK
+ - React Native SDK
+ validations:
+ required: true
+
+ - type: input
+ id: device-model
+ attributes:
+ label: Device Model
+ description: "Please provide the model of the device you are using."
+ placeholder: "e.g., iPhone 15, Samsung Galaxy S23"
+ validations:
+ required: true
+
+ - type: input
+ id: operating-system-version
+ attributes:
+ label: Operating System Version
+ description: "Please provide the version number of the operating system you are using."
+ placeholder: "e.g., iOS 16.1, Android 13"
+ validations:
+ required: true
+
+ - type: textarea
+ id: bug-description-reproduction
+ attributes:
+ label: Bug Description and Steps to Reproduce
+ description: "Provide a detailed description of the bug and a step-by-step guide on how to reproduce it."
+ placeholder: "Describe the bug in detail here...\n\nSteps to reproduce the bug on the client:\n1. Launch the application with specific configurations (mention any relevant config details).\n2. Perform the following actions in the app '...'.\n3. Observe the behavior and note any error messages or logs.\n4. Mention any additional setup relevant to the bug (e.g., SDK version, platform-specific settings)."
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: "If possible, please add screenshots to help explain your problem."
+
+ - type: textarea
+ id: screenshots-link
+ attributes:
+ label: Screenshots Link
+ description: "If applicable, please provide any links to screenshots here."
+ placeholder: "Paste your screenshot URL here, e.g., http://imgur.com/example"
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..6c91a8522
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,11 @@
+blank_issues_enabled: false
+contact_links:
+ # - name: "Bug Report"
+ # description: "Report a bug in the project"
+ # file: "bug-report.yml"
+ - name: 📢 Connect on slack
+ url: https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A
+ about: Support OpenIM-related requests or issues, get in touch with developers and help on slack
+ - name: 🌐 OpenIM Blog
+ url: https://www.openim.io/
+ about: Open the OpenIM community blog
diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md
new file mode 100644
index 000000000..e6f751e4e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.md
@@ -0,0 +1,20 @@
+---
+name: Documentation Update
+about: Propose updates to documentation, including README files and other docs.
+title: "[DOC]: " # Prefix for the title to help identify documentation issues
+labels: documentation # Labels to be automatically added
+assignees: '' # Optionally, specify maintainers or teams to be auto-assigned
+
+---
+
+## Documentation Updates
+Describe the documentation that needs to be updated or corrected. Please specify the files and sections if possible.
+
+## Motivation
+Explain why these updates are necessary. What is missing, misleading, or outdated?
+
+## Suggested Changes
+Detail the changes that you propose. If you are suggesting large changes, include examples or mockups of what the updated documentation should look like.
+
+## Additional Information
+Include any other information that might be relevant, such as links to discussions or related issues in the repository.
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml
new file mode 100644
index 000000000..18a96a965
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.yaml
@@ -0,0 +1,43 @@
+name: Feature Request
+title: "[FEATURE REQUEST] "
+labels: ["feature request","enhancement"]
+description: "Propose a new feature or improvement that you believe will help enhance the project."
+# assignees: []
+
+body:
+ - type: markdown
+ attributes:
+ 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."
+
+ - type: textarea
+ id: feature-reason
+ attributes:
+ label: Why this feature?
+ description: "Explain why this feature is needed. What problem does it solve? How does it benefit the project and its users?"
+ placeholder: "Describe the need for this feature..."
+ validations:
+ required: true
+
+ - type: textarea
+ id: solution-proposal
+ attributes:
+ label: Suggested Solution
+ description: "Describe your proposed solution for this feature. How do you envision it working?"
+ placeholder: "Detail your solution here..."
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: "Please provide any other relevant information or screenshots that could help illustrate your idea."
+
+ - type: textarea
+ id: additional-info
+ attributes:
+ label: Additional Information
+ description: "Include any additional information, links, or screenshots that might be relevant to your feature request."
+ placeholder: "Add more context or links to relevant resources..."
+
+ - type: markdown
+ attributes:
+ value: "Thank you for contributing to the project! We appreciate your input and will review your suggestion as soon as possible."
diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml
new file mode 100644
index 000000000..3a5690e8d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/other.yml
@@ -0,0 +1,29 @@
+name: 🐧 Other
+description: Use this for any other issues. Please do NOT create blank issues
+title: "[Other]: "
+labels: ["other"]
+# assignees: []
+
+body:
+ - type: markdown
+ attributes:
+ value: "# Other issue"
+ - type: textarea
+ id: issuedescription
+ attributes:
+ label: What would you like to share?
+ description: Provide a clear and concise explanation of your issue.
+ validations:
+ required: true
+ - type: textarea
+ id: extrainfo
+ attributes:
+ label: Additional information
+ description: Is there anything else we should know about this issue?
+ validations:
+ required: false
+ - type: markdown
+ attributes:
+ value: |
+ You can also join our Discord community [here](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A)
+ Feel free to check out other cool repositories of the openim Community [here](https://github.com/openimsdk)
diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md
new file mode 100644
index 000000000..760d89e53
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/rfc.md
@@ -0,0 +1,26 @@
+---
+name: RFC - Feature Proposal
+about: Submit a proposal for a significant feature to invite community discussion.
+title: "[RFC]: " # Prefix for the title to help identify RFC proposals
+labels: rfc, proposal # Labels to be automatically added
+assignees: '' # Optionally, specify maintainers or teams to be auto-assigned
+
+---
+
+## Proposal Overview
+Briefly describe the content and objectives of your proposal.
+
+## Motivation
+Why is this new feature necessary? What is the background of this problem?
+
+## Detailed Design
+Describe the technical details of the proposal, including implementation steps, code snippets, or architecture diagrams.
+
+## Alternatives Considered
+Have other alternatives been considered? Why is this approach preferred over others?
+
+## Impact
+How will this proposal affect existing practices and community users?
+
+## Additional Information
+Include any other relevant information such as related discussions, prior related work, etc.
diff --git a/.github/labels.yml b/.github/labels.yml
deleted file mode 100644
index 27e8c3835..000000000
--- a/.github/labels.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Refer to Kubernetes for size/* Settings
-# https://github.com/Kubernetes/Kubernetes
-XS:
- name: size/XS
- lines: 0
- color: 3CBF00
-S:
- name: size/S
- lines: 10
- color: 5D9801
-M:
- name: size/M
- lines: 30
- color: 7F7203
-L:
- name: size/L
- lines: 100
- color: A14C05
-XL:
- name: size/XL
- lines: 500
- color: C32607
-XXL:
- name: size/XXL
- lines: 1000
- color: E50009
- comment: |
- # Whoa! Easy there, Partner!
- This PR is too big. Please break it up into smaller PRs.
\ No newline at end of file
diff --git a/.github/workflows/auto-invite-comment.yml b/.github/workflows/auto-invite-comment.yml
new file mode 100644
index 000000000..dde36eaf1
--- /dev/null
+++ b/.github/workflows/auto-invite-comment.yml
@@ -0,0 +1,38 @@
+name: Invite users to join OpenIM Community.
+on:
+ issue_comment:
+ types:
+ - created
+jobs:
+ issue_comment:
+ name: Invite users to join OpenIM Community
+ if: ${{ github.event.comment.body == '/invite' || github.event.comment.body == '/close' || github.event.comment.body == '/comment' }}
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ steps:
+ - name: Invite user to join OpenIM Community
+ uses: peter-evans/create-or-update-comment@v4
+ with:
+ token: ${{ secrets.BOT_GITHUB_TOKEN }}
+ issue-number: ${{ github.event.issue.number }}
+ body: |
+ We value close connections with our users, developers, and contributors here at Open-IM-Server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
+
+ Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of Open-IM-Server. You can ask technical questions, seek help, or share your experiences with other users of Open-IM-Server.
+
+ In addition to Slack, we also offer the following ways to get in touch:
+
+ +
We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 Open-IM-Server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) team channel.
+ +
Get in touch with us on [Gmail](https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=winxu81@gmail.com). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
+ +
Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with Open-IM-Server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
+ +
Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of Open-IM-Server. We will process your request as soon as possible.
+
+ # - name: Close Issue
+ # uses: peter-evans/close-issue@v3
+ # with:
+ # token: ${{ secrets.BOT_GITHUB_TOKEN }}
+ # issue-number: ${{ github.event.issue.number }}
+ # comment: 🤖 Auto-closing issue, if you still need help please reopen the issue or ask for help in the community above
+ # labels: |
+ # accepted
diff --git a/.github/workflows/auto-invite.yml b/.github/workflows/auto-invite.yml
deleted file mode 100644
index f37271f1e..000000000
--- a/.github/workflows/auto-invite.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: Invite users to join our group
-on:
- issue_comment:
- types:
- - created
-jobs:
- issue_comment:
- name: Invite users to join our group
- if: ${{ github.event.comment.body == '/invite' || github.event.comment.body == '/close' || github.event.comment.body == '/comment' }}
- runs-on: ubuntu-latest
- permissions:
- issues: write
- steps:
-
- - name: Invite user to join our group
- uses: peter-evans/create-or-update-comment@v1
- with:
- token: ${{ secrets.BOT_GITHUB_TOKEN }}
- issue-number: ${{ github.event.issue.number }}
- body: |
- We value close connections with our users, developers, and contributors here at Open-IM-Server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
-
- Our most recommended /root/workspaces/openim/Open-IM-Server/docs copyway to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of Open-IM-Server. You can ask technical questions, seek help, or share your experiences with other users of Open-IM-Server.
-
- In addition to Slack, we also offer the following ways to get in touch:
-
- +
We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 Open-IM-Server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) team channel.
- +
Get in touch with us on [Gmail](https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=winxu81@gmail.com). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
- +
Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with Open-IM-Server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
- +
Add [Wechat](https://github.com/openimsdk/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of Open-IM-Server. We will process your request as soon as possible.
-
- - name: Close Issue
- uses: peter-evans/close-issue@v3
- with:
- token: ${{ secrets.BOT_GITHUB_TOKEN }}
- issue-number: ${{ github.event.issue.number }}
- comment: 🤖 Auto-closing issue, if you still need help please reopen the issue or ask for help in the community above
- labels: |
- triage/accepted
\ No newline at end of file
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
new file mode 100644
index 000000000..b97036d91
--- /dev/null
+++ b/.github/workflows/changelog.yml
@@ -0,0 +1,78 @@
+name: Release Changelog
+
+on:
+ release:
+ types: [released]
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ update-changelog:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Run Go Changelog Generator
+ run: |
+ # Run the Go changelog generator, passing the release tag if available
+ if [ "${{ github.event.release.tag_name }}" = "latest" ]; then
+ go run tools/changelog/changelog.go > "${{ github.event.release.tag_name }}-changelog.md"
+ else
+ go run tools/changelog/changelog.go "${{ github.event.release.tag_name }}" > "${{ github.event.release.tag_name }}-changelog.md"
+ fi
+
+ - name: Handle changelog files
+ run: |
+ # Ensure that the CHANGELOG directory exists
+ mkdir -p CHANGELOG
+
+ # Extract Major.Minor version by removing the 'v' prefix from the tag name
+ TAG_NAME=${{ github.event.release.tag_name }}
+ CHANGELOG_VERSION_NUMBER=$(echo "$TAG_NAME" | sed 's/^v//' | grep -oP '^\d+\.\d+')
+
+ # Define the new changelog file path
+ CHANGELOG_FILENAME="CHANGELOG-$CHANGELOG_VERSION_NUMBER.md"
+ CHANGELOG_PATH="CHANGELOG/$CHANGELOG_FILENAME"
+
+ # Check if the changelog file for the current release already exists
+ if [ -f "$CHANGELOG_PATH" ]; then
+ # If the file exists, append the new changelog to the existing one
+ cat "$CHANGELOG_PATH" >> "${TAG_NAME}-changelog.md"
+ # Overwrite the existing changelog with the updated content
+ mv "${TAG_NAME}-changelog.md" "$CHANGELOG_PATH"
+ else
+ # If the changelog file doesn't exist, rename the temp changelog file to the new changelog file
+ mv "${TAG_NAME}-changelog.md" "$CHANGELOG_PATH"
+
+ # Ensure that README.md exists
+ if [ ! -f "CHANGELOG/README.md" ]; then
+ echo -e "# CHANGELOGs\n\n" > CHANGELOG/README.md
+ fi
+
+ # Add the new changelog entry at the top of the README.md
+ if ! grep -q "\[$CHANGELOG_FILENAME\]" CHANGELOG/README.md; then
+ sed -i "3i- [$CHANGELOG_FILENAME](./$CHANGELOG_FILENAME)" CHANGELOG/README.md
+ # Remove the extra newline character added by sed
+ # sed -i '4d' CHANGELOG/README.md
+ fi
+ fi
+
+ - name: Clean up
+ run: |
+ # Remove any temporary files that were created during the process
+ rm -f "${{ github.event.release.tag_name }}-changelog.md"
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v7.0.5
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ commit-message: "Update CHANGELOG for release ${{ github.event.release.tag_name }}"
+ title: "Update CHANGELOG for release ${{ github.event.release.tag_name }}"
+ body: "This PR updates the CHANGELOG files for release ${{ github.event.release.tag_name }}"
+ branch: changelog-${{ github.event.release.tag_name }}
+ base: main
+ delete-branch: true
+ labels: changelog
diff --git a/.github/workflows/check-coverage.yml b/.github/workflows/check-coverage.yml
index d42469e20..505bf36b2 100644
--- a/.github/workflows/check-coverage.yml
+++ b/.github/workflows/check-coverage.yml
@@ -48,7 +48,7 @@
# uses: magnetikonline/action-golang-cache@v3
# with:
# go-version: ${{ env.GO_VERSION }}
-# token: ${{ secrets.BOT_GITHUB_TOKEN }}
+# token: ${{ secrets.BOT_TOKEN }}
# - name: Install Dependencies
# run: sudo apt update && sudo apt install -y libgpgme-dev libbtrfs-dev libdevmapper-dev
diff --git a/.github/workflows/cleanup-after-milestone-prs-merged.yml b/.github/workflows/cleanup-after-milestone-prs-merged.yml
new file mode 100644
index 000000000..c16acd0a7
--- /dev/null
+++ b/.github/workflows/cleanup-after-milestone-prs-merged.yml
@@ -0,0 +1,66 @@
+name: Cleanup After Milestone PRs Merged
+
+on:
+ pull_request:
+ types:
+ - closed
+
+jobs:
+ handle_pr:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4.2.0
+
+ - name: Get the PR title and extract PR numbers
+ id: extract_pr_numbers
+ run: |
+ # Get the PR title
+ PR_TITLE="${{ github.event.pull_request.title }}"
+
+ echo "PR Title: $PR_TITLE"
+
+ # Extract PR numbers from the title
+ PR_NUMBERS=$(echo "$PR_TITLE" | grep -oE "#[0-9]+" | tr -d '#' | tr '\n' ' ')
+ echo "Extracted PR Numbers: $PR_NUMBERS"
+
+ # Save PR numbers to a file
+ echo "$PR_NUMBERS" > pr_numbers.txt
+ echo "Saved PR Numbers to pr_numbers.txt"
+
+ # Check if the title matches the specific pattern
+ if echo "$PR_TITLE" | grep -qE "\[Created by @.+ from #[0-9]+\]$"; then
+ echo "proceed=true" >> $GITHUB_OUTPUT
+ else
+ echo "proceed=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Use extracted PR numbers and label PRs
+ if: (steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge')) && github.event.pull_request.merged == true
+ run: |
+ # Read the previously saved PR numbers
+ PR_NUMBERS=$(cat pr_numbers.txt)
+ echo "Using extracted PR Numbers: $PR_NUMBERS"
+
+ # Loop through each PR number and add label
+ for PR_NUMBER in $PR_NUMBERS; do
+ echo "Adding 'cherry-picked' label to PR #$PR_NUMBER"
+ curl -X POST \
+ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
+ -H "Accept: application/vnd.github+json" \
+ https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/labels \
+ -d '{"labels":["cherry-picked"]}'
+ done
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Delete branch after PR close
+ if: steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge')
+ continue-on-error: true
+ run: |
+ BRANCH_NAME="${{ github.event.pull_request.head.ref }}"
+ echo "Branch to delete: $BRANCH_NAME"
+ git push origin --delete "$BRANCH_NAME"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 6233ffcad..fd871e2b5 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,76 +1,67 @@
-# Copyright © 2023 OpenIM open source community. All rights reserved.
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
-name: "Code Scanning - Action"
+name: "CodeQL"
on:
push:
- branches: [main]
+ branches: [ main ]
pull_request:
- branches: [main]
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
schedule:
- # ┌───────────── minute (0 - 59)
- # │ ┌───────────── hour (0 - 23)
- # │ │ ┌───────────── day of the month (1 - 31)
- # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
- # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
- # │ │ │ │ │
- # │ │ │ │ │
- # │ │ │ │ │
- # * * * * *
- - cron: '30 1 * * 0'
+ - cron: '18 19 * * 6'
jobs:
- CodeQL-Build:
- # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
+ analyze:
+ name: Analyze
runs-on: ubuntu-latest
- permissions:
- # required for all workflows
- security-events: write
-
- # only required for workflows in private repositories
- actions: read
- contents: read
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'go' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+ # Learn more:
+ # 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
steps:
- - name: Checkout repository
- uses: actions/checkout@v3
+ - name: Checkout repository
+ uses: actions/checkout@v4
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
- # Override language selection by uncommenting this and choosing your languages
- # with:
- # languages: go, javascript, csharp, python, cpp, java, ruby
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
- # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
- # If this step fails, then you should remove it and run the build manually (see below).
- - name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
- # ✏️ If the Autobuild fails above, remove it and uncomment the following
- # three lines and modify them (or add more) to build your code if your
- # project uses a compiled language
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
- #- run: |
- # make bootstrap
- # make release
+ #- run: |
+ # make bootstrap
+ # make release
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
\ No newline at end of file
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
\ No newline at end of file
diff --git a/.github/workflows/comment-check.yml b/.github/workflows/comment-check.yml
new file mode 100644
index 000000000..aca87a508
--- /dev/null
+++ b/.github/workflows/comment-check.yml
@@ -0,0 +1,157 @@
+name: Non-English Comments Check
+
+on:
+ pull_request_target:
+ types: [opened, synchronize, reopened]
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ non-english-comments-check:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+
+ env:
+ # Directories to be excluded
+ EXCLUDE_DIRS: ".git docs tests scripts assets node_modules build"
+ # Files to be excluded
+ EXCLUDE_FILES: ".md .txt .html .css .min.js .mdx"
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ fetch-depth: 0
+
+ - name: Search for Non-English comments in the entire repository
+ run: |
+ set -e
+ # Define the regex pattern to match Chinese characters
+ pattern='[\p{Han}]'
+
+ # Use find to get all files in the repository
+ all_files=$(find . -type f)
+
+ # Loop over each file in the repository
+ for file in $all_files; do
+ # Skip files in excluded directories
+ skip_file=false
+ for dir in ${EXCLUDE_DIRS}; do
+ if [[ "$file" == ./$dir/* ]]; then
+ skip_file=true
+ break
+ fi
+ done
+
+ # Skip files matching excluded patterns
+ for file_pattern in ${EXCLUDE_FILES}; do
+ if [[ "$file" == *$file_pattern ]]; then
+ skip_file=true
+ break
+ fi
+ done
+
+ # If the file matches any exclude pattern, skip it
+ if [ "$skip_file" = true ]; then
+ continue
+ fi
+
+ # Use grep to find all comments containing Non-English characters in filtered files
+ grep_output=$(grep -PnH "$pattern" "$file" || true)
+ if [ -n "$grep_output" ]; then
+ # Insert a tab after the line number, keeping the colon between the file path and line number
+ formatted_output=$(echo "$grep_output" | sed 's/^\(.*:[0-9]\+\):/\1\t/')
+ echo "$formatted_output" >> non_english_comments.txt # Save to file
+ fi
+ done
+
+ # - name: Search for Non-English comments in PR diff files
+ # run: |
+ # set -e
+ # # Define the regex pattern to match Chinese characters
+ # pattern='[\p{Han}]'
+
+ # # Get the list of files changed in this PR compared to the base branch
+ # changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})
+
+
+ # # Loop over each changed file
+ # for file in $changed_files; do
+ # # Skip files in excluded directories
+ # skip_file=false
+ # for dir in ${EXCLUDE_DIRS}; do
+ # if [[ "$file" == ./$dir/* ]]; then
+ # skip_file=true
+ # break
+ # fi
+ # done
+
+ # # Skip files matching excluded patterns
+ # for file_pattern in ${EXCLUDE_FILES}; do
+ # if [[ "$file" == *$file_pattern ]]; then
+ # skip_file=true
+ # break
+ # fi
+ # done
+
+ # # If the file matches any exclude pattern, skip it
+ # if [ "$skip_file" = true ]; then
+ # continue
+ # fi
+
+ # # Use grep to find all comments containing Non-English characters in filtered files
+ # grep_output=$(grep -PnH "$pattern" "$file" || true)
+ # if [ -n "$grep_output" ]; then
+ # # Insert a tab after the line number, keeping the colon between the file path and line number
+ # formatted_output=$(echo "$grep_output" | sed 's/^\(.*:[0-9]\+\):/\1\t/')
+ # echo "$formatted_output" >> non_english_comments.txt # Save to file
+ # fi
+ # done
+
+ - name: Store non-English comments in ENV
+ run: |
+ # Store the entire content of non_english_comments.txt into an environment variable
+ if [ -f non_english_comments.txt ]; then
+ NON_ENGLISH_COMMENTS=$(cat non_english_comments.txt)
+ echo "NON_ENGLISH_COMMENTS<> $GITHUB_ENV
+ echo "$NON_ENGLISH_COMMENTS" >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+ fi
+
+ - name: Output non-English comments if found
+ run: |
+ if [ -s non_english_comments.txt ]; then
+ echo "Non-English comments found in the following locations:"
+ cat non_english_comments.txt
+ exit 1 # terminate the workflow
+ else
+ echo "No Non-English comments found."
+ fi
+
+ - name: Find Comment
+ if: failure() && github.event_name != 'workflow_dispatch'
+ uses: peter-evans/find-comment@v3.1.0
+ id: fc
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ comment-author: 'OpenIM-Robot'
+ body-includes: Non-English comments were found in the following locations
+
+ - name: Comment on PR if errors found
+ if: failure() && github.event_name != 'workflow_dispatch' # This step runs only if the previous step fails
+ uses: peter-evans/create-or-update-comment@v4.0.0
+ with:
+ # token: ${{ secrets.GITHUB_TOKEN }} # GitHub token to post the comment
+ token: ${{ secrets.BOT_TOKEN }}
+ issue-number: ${{ github.event.pull_request.number }} # PR number
+ comment-id: ${{ steps.fc.outputs.comment-id }}
+ edit-mode: replace # This ensures that the comment is updated instead of creating a new one
+ body: |
+ ⚠️ Non-English comments were found in the following locations:
+ ```
+ ${{ env.NON_ENGLISH_COMMENTS }}
+ ```
\ No newline at end of file
diff --git a/.github/workflows/delete-users-invalid-comments.yml b/.github/workflows/delete-users-invalid-comments.yml
new file mode 100644
index 000000000..c7e0c7d61
--- /dev/null
+++ b/.github/workflows/delete-users-invalid-comments.yml
@@ -0,0 +1,85 @@
+name: Delete User Comments
+
+permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+
+on:
+ workflow_dispatch:
+ inputs:
+ username:
+ description: "GitHub username of the user whose comments need to be deleted"
+ required: true
+ default: "username_to_delete"
+ issue_pr_range:
+ description: "Comma-separated list of issue/PR numbers to limit deletion scope (optional)"
+ required: false
+ default: ""
+
+env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ TEMP_DIR: /tmp # Temporary directory for storing intermediate data
+
+jobs:
+ delete_user_comments:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Setup Temporary Directory
+ run: mkdir -p ${{ env.TEMP_DIR }}
+
+ - name: Determine Scope and Fetch Data
+ run: |
+ username="${{ github.event.inputs.username }}"
+ range="${{ github.event.inputs.issue_pr_range }}"
+
+ if [ -n "$range" ]; then
+ echo "Limiting scope to specified range: $range"
+ echo "$range" | tr ',' '\n' > ${{ env.TEMP_DIR }}/range.txt
+ else
+ echo "Fetching all issues and pull requests for repository: ${{ github.repository }}"
+
+ # Fetch all issues
+ issues=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/issues?state=all&per_page=100")
+
+ # Save issue and PR numbers separately
+ echo "$issues" | jq -r '.[] | select(.pull_request == null) | .number' > ${{ env.TEMP_DIR }}/issues.txt
+ echo "$issues" | jq -r '.[] | select(.pull_request != null) | .number' > ${{ env.TEMP_DIR }}/pull_requests.txt
+
+ cat ${{ env.TEMP_DIR }}/issues.txt ${{ env.TEMP_DIR }}/pull_requests.txt > ${{ env.TEMP_DIR }}/range.txt
+ fi
+
+ - name: Delete Comments by User
+ run: |
+ username="${{ github.event.inputs.username }}"
+ echo "Deleting comments by user: $username"
+
+ for number in $(cat ${{ env.TEMP_DIR }}/range.txt); do
+ echo "Processing Issue/PR #$number"
+
+ # Fetch comments for the issue/PR
+ comments=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/$number/comments")
+
+ # Filter and delete user comments
+ echo "$comments" | jq -c ".[] | select(.user.login == \"$username\")" | while read comment; do
+ comment_id=$(echo "$comment" | jq -r '.id')
+ echo "Deleting comment ID: $comment_id from Issue/PR #$number"
+
+ response=$(curl -s -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/comments/$comment_id")
+
+ if [ -z "$response" ]; then
+ echo "Successfully deleted comment ID: $comment_id"
+ else
+ echo "Failed to delete comment ID: $comment_id. Response: $response"
+ fi
+ done
+ done
+
+ - name: Completion
+ run: echo "All comments by user ${{ github.event.inputs.username }} in the specified scope have been deleted."
diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml
deleted file mode 100644
index a5a327599..000000000
--- a/.github/workflows/e2e-test.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
\ No newline at end of file
diff --git a/.github/workflows/go-build-test.yml b/.github/workflows/go-build-test.yml
new file mode 100644
index 000000000..69ac684f5
--- /dev/null
+++ b/.github/workflows/go-build-test.yml
@@ -0,0 +1,151 @@
+name: Go Build Test
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ go-build:
+ name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ go_version: ["1.21.x", "1.22.x"]
+
+ steps:
+ - name: Checkout Server repository
+ uses: actions/checkout@v4
+
+ - name: Set up Go ${{ matrix.go_version }}
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go_version }}
+
+ - name: Build test SDK core
+ run: |
+ go mod tidy
+ go generate ./...
+ cd wasm/cmd && make wasm
+
+ # TODO: add coverage test
+ go-test:
+ name: Benchmark Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write
+ env:
+ SERVER_DIR: open-im-server
+ CONFIG_PATH: config/notification.yml
+ # pull-requests: write
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ go_version: ["1.22.x"]
+
+ steps:
+ - name: Checkout SDK repository
+ uses: actions/checkout@v4
+
+ - name: Checkout Server repository
+ uses: actions/checkout@v4
+ with:
+ repository: "openimsdk/open-im-server"
+ path: ${{ env.SERVER_DIR }}
+
+ - name: Set up Go ${{ matrix.go_version }}
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go_version }}
+
+ - name: Get Server dependencies
+ run: |
+ cd ${{ env.SERVER_DIR }}
+ go install github.com/magefile/mage@latest
+ go mod download
+
+ - name: Install yq
+ run: |
+ sudo wget https://github.com/mikefarah/yq/releases/download/v4.34.1/yq_linux_amd64 -O /usr/bin/yq
+ sudo chmod +x /usr/bin/yq
+
+ - name: Modify Server Configuration
+ run: |
+ cd ${{ env.SERVER_DIR }}
+ yq e '.groupCreated.isSendMsg = true' -i ${{ env.CONFIG_PATH }}
+ yq e '.friendApplicationApproved.isSendMsg = true' -i ${{ env.CONFIG_PATH }}
+
+ - name: Start Server Services
+ run: |
+ cd ${{ env.SERVER_DIR }}
+ docker compose up -d
+ mage build
+ mage start
+ mage check
+
+ - name: Build test SDK core
+ run: |
+ go mod tidy
+ cd integration_test
+ mkdir data
+ go run main.go -lgr 0.8 -imf -crg -ckgn -ckcon -sem -ckmsn -u 20 -su 5 -lg 2 -cg 2 -cgm 3 -sm 10 -gm 10 -reg
+
+# - name: Stop Server
+# run: |
+# cd ${{ github.workspace }}/open-im-server
+# mage stop
+
+# dockerfile-test:
+# name: Build and Test Dockerfile
+# runs-on: ubuntu-latest
+# strategy:
+# matrix:
+# go_version: ["1.21"]
+
+# steps:
+# - name: Checkout Repository
+# uses: actions/checkout@v4
+
+# - name: Set up Go ${{ matrix.go_version }}
+# uses: actions/setup-go@v5
+# with:
+# go-version: ${{ matrix.go_version }}
+
+# - name: Get dependencies
+# run: |
+# go mod tidy
+# go mod download
+# go install github.com/magefile/mage@latest
+
+# - name: Build Docker Image
+# run: |
+# IMAGE_NAME="${{ github.event.repository.name }}-test"
+# CONTAINER_NAME="${{ github.event.repository.name }}-container"
+# docker build -t $IMAGE_NAME .
+
+# - name: Run Docker Container
+# run: |
+# IMAGE_NAME="${{ github.event.repository.name }}-test"
+# CONTAINER_NAME="${{ github.event.repository.name }}-container"
+# docker run --name $CONTAINER_NAME -d $IMAGE_NAME
+# docker ps -a
+
+# - name: Test Docker Container Logs
+# run: |
+# CONTAINER_NAME="${{ github.event.repository.name }}-container"
+# docker logs $CONTAINER_NAME
+
+# # - name: Cleanup Docker Container
+# # run: |
+# # CONTAINER_NAME="${{ github.event.repository.name }}-container"
+# # IMAGE_NAME="${{ github.event.repository.name }}-test"
+# # docker stop $CONTAINER_NAME
+# # docker rm $CONTAINER_NAME
+# # docker rmi $IMAGE_NAME
diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml
deleted file mode 100644
index e76b6fff5..000000000
--- a/.github/workflows/gosec.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# name: Run gosec
-
-# # gosec is a source code security audit tool for the Go language. It performs a static
-# # analysis of the Go code, looking for potential security problems. The main functions of gosec are:
-# # 1. Find common security vulnerabilities, such as SQL injection, command injection, and cross-site scripting (XSS).
-# # 2. Audit codes according to common security standards and find non-standard codes.
-# # 3. Assist the Go language engineer to write safe and reliable code.
-
-# on:
-# push:
-# branches: "*"
-# pull_request:
-# branches: "*"
-# paths-ignore:
-# - '*.md'
-# - '*.yml'
-# - '.github'
-
-# jobs:
-# golang-security-action:
-# runs-on: ubuntu-latest
-# env:
-# GO111MODULE: on
-# steps:
-# - name: Check out code
-# uses: actions/checkout@v3
-# - name: Run Gosec Security Scanner
-# uses: securego/gosec@master
-# with:
-# args: ./...
\ No newline at end of file
diff --git a/.github/workflows/issue-robot.yml b/.github/workflows/issue-robot.yml
deleted file mode 100644
index c4b5914b5..000000000
--- a/.github/workflows/issue-robot.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: 'issue translator'
-on:
- issue_comment:
- types: [created]
- issues:
- types: [opened]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: usthe/issues-translate-action@v2.7
- with:
- # it is not necessary to decide whether you need to modify the issue header content
- IS_MODIFY_TITLE: true
- BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
- # Required, input your bot github token
\ No newline at end of file
diff --git a/.github/workflows/issue-translator.yml b/.github/workflows/issue-translator.yml
new file mode 100644
index 000000000..6a8528ae6
--- /dev/null
+++ b/.github/workflows/issue-translator.yml
@@ -0,0 +1,19 @@
+name: 'issue-translator'
+on:
+ issue_comment:
+ types: [created]
+ issues:
+ types: [opened]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: usthe/issues-translate-action@v2.7
+ with:
+ BOT_GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}
+ IS_MODIFY_TITLE: true
+ # not require, default false, . Decide whether to modify the issue title
+ # if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
+ CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
+ # not require. Customize the translation robot prefix message.
\ No newline at end of file
diff --git a/.github/workflows/link-pr.yml b/.github/workflows/link-pr.yml
deleted file mode 100644
index 51eddf159..000000000
--- a/.github/workflows/link-pr.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# name: Github Rebot for Link check error
-
-# on:
-# pull_request:
-# branches: [ main ]
-# paths:
-# - '**.md'
-# - 'docs/**'
-# - '.lycheeignore'
-# push:
-# branches: [ main ]
-
-# schedule:
-# - cron: '0 11 * * *'
-
-# jobs:
-# linkChecker:
-# runs-on: ubuntu-latest
-# steps:
-# - uses: actions/checkout@v3
-
-# - name: Link Checker
-# id: lychee
-# uses: lycheeverse/lychee-action@v1.7.0
-# with:
-# # For parameter description, see https://github.com/lycheeverse/lychee#commandline-parameters
-# # Actions Link address -> https://github.com/lycheeverse/lychee-action
-# # -E, --exclude-all-private Exclude all private IPs from checking.
-# # -i, --insecure Proceed for server connections considered insecure (invalid TLS)
-# # -n, --no-progress Do not show progress bar.
-# # -t, --timeout Website timeout in seconds from connect to response finished [default:20]
-# # --max-concurrency Maximum number of concurrent network requests [default: 128]
-# # -a --accept Comma-separated list of accepted status codes for valid links
-# # docs/.vitepress/dist the site directory to check
-# # ./*.md all markdown files in the root directory
-# args: --verbose -E -i --no-progress --exclude-path './CHANGELOG' './**/*.md'
-# env:
-# GITHUB_TOKEN: ${{secrets.GH_PAT}}
-
-# - name: Create Issue From File
-# if: env.lychee_exit_code != 0
-# uses: peter-evans/create-issue-from-file@v4
-# with:
-# title: Bug reports for links in OpenIM docs
-# content-filepath: ./lychee/out.md
-# labels: kind/documentation, triage/unresolved, report
-# token: ${{ secrets.BOT_GITHUB_TOKEN }}
diff --git a/.github/workflows/merge-from-milestone.yml b/.github/workflows/merge-from-milestone.yml
new file mode 100644
index 000000000..68410dc1b
--- /dev/null
+++ b/.github/workflows/merge-from-milestone.yml
@@ -0,0 +1,165 @@
+name: Create Pre-Release PR from Milestone
+
+permissions:
+ contents: write
+ pull-requests: write
+ issues: write
+
+on:
+ workflow_dispatch:
+ inputs:
+ milestone_name:
+ description: "Milestone name to collect closed PRs from"
+ required: true
+ default: "v3.8.4"
+ target_branch:
+ description: "Target branch to merge the consolidated PR"
+ required: true
+ default: "pre-release-v3.8.4"
+
+env:
+ MILESTONE_NAME: ${{ github.event.inputs.milestone_name || 'v3.8.4' }}
+ TARGET_BRANCH: ${{ github.event.inputs.target_branch || 'pre-release-v3.8.4' }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
+ LABEL_NAME: cherry-picked
+ TEMP_DIR: /tmp
+
+jobs:
+ merge_milestone_prs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Setup temp directory
+ run: |
+ # Create the temporary directory and initialize necessary files
+ mkdir -p ${{ env.TEMP_DIR }}
+ touch ${{ env.TEMP_DIR }}/pr_numbers.txt
+ touch ${{ env.TEMP_DIR }}/commit_hashes.txt
+ touch ${{ env.TEMP_DIR }}/pr_title.txt
+ touch ${{ env.TEMP_DIR }}/pr_body.txt
+ touch ${{ env.TEMP_DIR }}/created_pr_number.txt
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.BOT_TOKEN }}
+
+ - name: Setup Git User for OpenIM-Robot
+ run: |
+ git config --global user.email "OpenIM-Robot@users.noreply.github.com"
+ git config --global user.name "OpenIM-Robot"
+
+ - name: Fetch Milestone ID and Filter PR Numbers
+ env:
+ MILESTONE_NAME: ${{ env.MILESTONE_NAME }}
+ run: |
+ # Fetch milestone details and extract milestone ID
+ milestones=$(curl -s -H "Authorization: token $BOT_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/milestones")
+ milestone_id=$(echo "$milestones" | grep -B3 "\"title\": \"$MILESTONE_NAME\"" | grep '"number":' | head -n1 | grep -o '[0-9]\+')
+ if [ -z "$milestone_id" ]; then
+ echo "Milestone '$MILESTONE_NAME' not found. Exiting."
+ exit 1
+ fi
+ echo "Milestone ID: $milestone_id"
+ echo "MILESTONE_ID=$milestone_id" >> $GITHUB_ENV
+
+ # Fetch issues for the milestone
+ issues=$(curl -s -H "Authorization: token $BOT_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/issues?milestone=$milestone_id&state=closed&per_page=100")
+
+ > ${{ env.TEMP_DIR }}/pr_numbers.txt
+
+ # Filter PRs that do not have the 'cherry-picked' label
+ for pr_number in $(echo "$issues" | jq -r '.[] | select(.pull_request != null) | .number'); do
+ labels=$(curl -s -H "Authorization: token $BOT_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels" | jq -r '.[].name')
+
+ if ! echo "$labels" | grep -q "${LABEL_NAME}"; then
+ echo "PR #$pr_number does not have the 'cherry-picked' label. Adding to the list."
+ echo "$pr_number" >> ${{ env.TEMP_DIR }}/pr_numbers.txt
+ fi
+ done
+
+ sort -n ${{ env.TEMP_DIR }}/pr_numbers.txt -o ${{ env.TEMP_DIR }}/pr_numbers.txt
+
+ - name: Create Individual PRs
+ run: |
+ for pr_number in $(cat ${{ env.TEMP_DIR }}/pr_numbers.txt); do
+ pr_details=$(curl -s -H "Authorization: token $BOT_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ "https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number")
+ pr_title=$(echo "$pr_details" | jq -r '.title')
+ pr_body=$(echo "$pr_details" | jq -r '.body')
+ pr_creator=$(echo "$pr_details" | jq -r '.user.login')
+ merge_commit=$(echo "$pr_details" | jq -r '.merge_commit_sha')
+ short_commit_hash=$(echo "$merge_commit" | cut -c 1-7)
+
+ if [ "$merge_commit" != "null" ]; then
+ git fetch origin
+
+ echo "Checking out target branch: $TARGET_BRANCH"
+ git checkout $TARGET_BRANCH
+
+ echo "Pulling latest changes from target branch: $TARGET_BRANCH"
+ git pull origin $TARGET_BRANCH
+
+ cherry_pick_branch="cherry-pick-${short_commit_hash}"
+ git checkout -b $cherry_pick_branch
+
+ echo "Cherry-picking commit: $merge_commit"
+ if ! git cherry-pick "$merge_commit" --strategy=recursive -X theirs; then
+ echo "Conflict detected for $merge_commit. Resolving with incoming changes."
+ conflict_files=$(git diff --name-only --diff-filter=U)
+ echo "Conflicting files:"
+ echo "$conflict_files"
+
+ for file in $conflict_files; do
+ if [ -f "$file" ]; then
+ echo "Resolving conflict for $file"
+ git add "$file"
+ else
+ echo "File $file has been deleted. Skipping."
+ git rm "$file"
+ fi
+ done
+
+ echo "Conflicts resolved. Continuing cherry-pick."
+ git cherry-pick --continue || { echo "Cherry-pick failed, but continuing to create PR."; }
+ else
+ echo "Cherry-pick successful for commit $merge_commit."
+ fi
+
+ git remote set-url origin "https://${BOT_TOKEN}@github.com/${{ github.repository }}.git"
+
+ echo "Pushing branch: $cherry_pick_branch"
+ if ! git push origin $cherry_pick_branch --force; then
+ echo "Push failed, but continuing to create PR..."
+ fi
+
+ new_pr_title="$pr_title [Created by @$pr_creator from #$pr_number]"
+ new_pr_body="$pr_body
+ > This PR is created from original PR #$pr_number."
+
+ response=$(curl -s -X POST -H "Authorization: token $BOT_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ https://api.github.com/repos/${{ github.repository }}/pulls \
+ -d "$(jq -n --arg title "$new_pr_title" \
+ --arg head "$cherry_pick_branch" \
+ --arg base "$TARGET_BRANCH" \
+ --arg body "$new_pr_body" \
+ '{title: $title, head: $head, base: $base, body: $body}')")
+
+ new_pr_number=$(echo "$response" | jq -r '.number')
+ echo "Created PR #$new_pr_number"
+
+ curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
+ -H "Accept: application/vnd.github+json" \
+ -d '{"labels": ["milestone-merge"]}' \
+ "https://api.github.com/repos/${{ github.repository }}/issues/$new_pr_number/labels"
+ fi
+ done
diff --git a/.github/workflows/opencommit.yml b/.github/workflows/opencommit.yml
deleted file mode 100644
index d90143f61..000000000
--- a/.github/workflows/opencommit.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright © 2023 OpenIM open source community. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: 'OpenIM Commit Action'
-
-on:
- push:
- branches:
- - main
-
-jobs:
- opencommit:
- timeout-minutes: 10
- name: OpenCommit
- runs-on: ubuntu-latest
- permissions: write-all
- steps:
- - name: Setup Node.js Environment
- uses: actions/setup-node@v2
- with:
- node-version: '16'
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0
- - uses: di-sukharev/opencommit@github-action-v1.0.4
- with:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- env:
- # set openAI api key in repo actions secrets,
- # for openAI keys go to: https://platform.openai.com/account/api-keys
- # for repo secret go to: https://github.com/kuebcub/settings/secrets/actions
- OCO_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
-
- # customization
- OCO_OPENAI_MAX_TOKENS: 500
- OCO_OPENAI_BASE_PATH: ''
- OCO_DESCRIPTION: false
- OCO_EMOJI: false
- OCO_MODEL: gpt-3.5-turbo
- OCO_LANGUAGE: en
\ No newline at end of file
diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml
deleted file mode 100644
index 006b1bef2..000000000
--- a/.github/workflows/openimci.yml
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright © 2023 OpenIM open source community. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: OpenIM CI Aotu Build and Install
-
-on:
- push:
- branches:
- - main
- paths-ignore:
- - "docs/**"
- - "README.md"
- - "README_zh-CN.md"
- - "CONTRIBUTING.md"
- pull_request:
- branches:
- - main
- paths-ignore:
- - "README.md"
- - "README_zh-CN.md"
- - "CONTRIBUTING.md"
- - "docs/**"
-
-env:
- GO_VERSION: "1.19"
- GOLANGCI_VERSION: "v1.50.1"
-
-jobs:
- openim:
- name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- permissions:
- # Give the default GITHUB_TOKEN write permission to commit and push the changed files back to the repository.
- contents: write
- environment:
- name: openim
-
- strategy:
- matrix:
- go_version: ["1.18","1.19","1.20","1.21"]
- os: [ubuntu-latest]
-
- steps:
- - name: Set up Go ${{ matrix.go_version }}
- uses: actions/setup-go@v2
- with:
- go-version: ${{ matrix.go_version }}
- id: go
-
- - name: Check out code into the Go module directory
- uses: actions/checkout@v3
-
- - name: Install Task
- uses: arduino/setup-task@v1
- with:
- version: 2.x
-
- - name: Run go format
- run: |
- sudo make format
- echo "Run go format successfully"
- continue-on-error: true
-
- - name: Generate all necessary files, such as error code files
- run: |
- make generate
- echo "Generate all necessary files successfully"
- continue-on-error: true
-
- - name: Run unit test and get test coverage
- run: |
- make cover
- echo "Run unit test and get test coverage successfully"
- continue-on-error: true
-
- - name: Clean all build
- run: |
- sudo make clean
- echo "Clean all build successfully"
-
- - name: Build source code for host platform
- run: |
- sudo make build
- echo "Build source code for host platform successfully"
-
- - name: Build wasm source code
- run: |
- sudo make build-wasm
- echo "Build wasm source code successfully"
-
- - name: OpenIM verify copyright
- run: |
- sudo make verify-copyright
- sudo make add-copyright
- echo "OpenIM verify successfully"
- continue-on-error: true
-
- - name: push OpenIM
- uses: stefanzweifel/git-auto-commit-action@v4
- with:
- commit_message: "cicd: robot automated Change"
- # commit_options: '--no-verify --signoff'
- branch: main
- # create_branch: true
- # # Optional commit user and author settings
- # commit_user_name: kubbot # defaults to "github-actions[bot]"
- # commit_user_email: 3293172751ysy@gmail.com # defaults to "41898282+github-actions[bot]@users.noreply.github.com"
- # commit_author: Kubbot # defaults to author of the commit that triggered the run
- continue-on-error: true
-
- - name: Set Current Directory
- id: set_directory
- run: |
- echo "::set-output name=directory::$(pwd)"
- continue-on-error: true
-
- - name: Collect Test Coverage File
- id: collect_coverage
- run: |
- cd ${{ steps.set_directory.outputs.directory }}
- make cover
- echo "::set-output name=coverage_file::./_output/tmp/coverage.out"
- continue-on-error: true
-
- - name: Display Test Coverage
- run: |
- echo "Test Coverage:"
- cat ${{ steps.collect_coverage.outputs.coverage_file }}
- continue-on-error: true
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
- continue-on-error: true
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0819f47e1..fd041f8a0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,22 +1,7 @@
-# Copyright © 2023 OpenIM open source community. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: OpenIM OpenIM Core release
+name: OpenIM release
on:
push:
- # run only against tags
tags:
- '*'
@@ -25,7 +10,6 @@ permissions:
packages: write
issues: write
-
jobs:
goreleaser:
runs-on: ubuntu-latest
@@ -41,42 +25,10 @@ jobs:
# on your needs.
- uses: goreleaser/goreleaser-action@v4
with:
- # either 'goreleaser' (default) or 'goreleaser-pro':
distribution: goreleaser
version: latest
workdir: .
- args: release --clean --clean --release-footer-tmpl=scripts/template/footer.md.tmpl --release-header-tmpl=scripts/template/head.md.tmpl
+ args: release --clean
env:
USERNAME: ${{ github.repository_owner }}
- GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
- FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
- # Your GoReleaser Pro key, if you are using the 'goreleaser-pro'
- # distribution:
- # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
-
- goreleaser-check-pkgs:
- runs-on: ubuntu-latest
- env:
- DOCKER_CLI_EXPERIMENTAL: "enabled"
- needs: [ goreleaser ]
- if: github.ref == 'refs/heads/main'
- strategy:
- matrix:
- format: [ deb, rpm, apk ]
- steps:
- - uses: actions/checkout@v3 # v3
- with:
- fetch-depth: 0
- - uses: arduino/setup-task@e26d8975574116b0097a1161e0fe16ba75d84c1c # v1
- with:
- version: 3.x
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- - uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
- with:
- path: |
- ./_output/dist/*.deb
- ./_output/dist/*.rpm
- ./_output/dist/*.apk
- key: ${{ github.ref }}
- - run: task goreleaser:test:${{ matrix.format }}
\ No newline at end of file
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/remove-unused-labels.yml b/.github/workflows/remove-unused-labels.yml
new file mode 100644
index 000000000..a2259d718
--- /dev/null
+++ b/.github/workflows/remove-unused-labels.yml
@@ -0,0 +1,75 @@
+name: Remove Unused Labels
+on:
+ workflow_dispatch:
+
+jobs:
+ cleanup:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ contents: read
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Fetch All Issues and PRs
+ id: fetch_issues_prs
+ uses: actions/github-script@v7.0.1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issues = await github.paginate(github.rest.issues.listForRepo, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'all',
+ per_page: 100
+ });
+
+ const labelsInUse = new Set();
+ issues.forEach(issue => {
+ issue.labels.forEach(label => {
+ labelsInUse.add(label.name);
+ });
+ });
+
+ return JSON.stringify(Array.from(labelsInUse));
+ result-encoding: string
+
+ - name: Fetch All Labels
+ id: fetch_labels
+ uses: actions/github-script@v7.0.1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const labels = await github.paginate(github.rest.issues.listLabelsForRepo, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ per_page: 100
+ });
+
+ return JSON.stringify(labels.map(label => label.name));
+ result-encoding: string
+
+ - name: Remove Unused Labels
+ uses: actions/github-script@v7.0.1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const labelsInUse = new Set(JSON.parse(process.env.LABELS_IN_USE));
+ const allLabels = JSON.parse(process.env.ALL_LABELS);
+
+ const unusedLabels = allLabels.filter(label => !labelsInUse.has(label));
+
+ for (const label of unusedLabels) {
+ await github.rest.issues.deleteLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: label
+ });
+ console.log(`Deleted label: ${label}`);
+ }
+
+ env:
+ LABELS_IN_USE: ${{ steps.fetch_issues_prs.outputs.result }}
+ ALL_LABELS: ${{ steps.fetch_labels.outputs.result }}
\ No newline at end of file
diff --git a/.github/workflows/reopen-issue.yml b/.github/workflows/reopen-issue.yml
new file mode 100644
index 000000000..8531a3ee1
--- /dev/null
+++ b/.github/workflows/reopen-issue.yml
@@ -0,0 +1,78 @@
+name: Reopen and Update Stale Issues
+
+on:
+ workflow_dispatch: # manually trigger
+
+jobs:
+ reopen_stale_issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ contents: read
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Fetch Closed Issues with lifecycle/stale Label
+ id: fetch_issues
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issues = await github.paginate(github.rest.issues.listForRepo, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'closed',
+ labels: 'lifecycle/stale',
+ per_page: 100
+ });
+ const issueNumbers = issues
+ .filter(issue => !issue.pull_request) // exclude PR
+ .map(issue => issue.number);
+ console.log(`Fetched issues: ${issueNumbers}`);
+ return issueNumbers;
+
+ - name: Set issue numbers
+ id: set_issue_numbers
+ run: |
+ echo "ISSUE_NUMBERS=${{ steps.fetch_issues.outputs.result }}" >> $GITHUB_ENV
+ echo "Issue numbers: ${{ steps.fetch_issues.outputs.result }}"
+
+ - name: Reopen Issues
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issueNumbers = JSON.parse(process.env.ISSUE_NUMBERS);
+ console.log(`Reopening issues: ${issueNumbers}`);
+
+ for (const issue_number of issueNumbers) {
+ // Reopen the issue
+ await github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issue_number,
+ state: 'open'
+ });
+ console.log(`Reopened issue #${issue_number}`);
+ }
+
+ - name: Remove lifecycle/stale Label
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issueNumbers = JSON.parse(process.env.ISSUE_NUMBERS);
+ console.log(`Removing 'lifecycle/stale' label from issues: ${issueNumbers}`);
+
+ for (const issue_number of issueNumbers) {
+ // Remove the lifecycle/stale label
+ await github.rest.issues.removeLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issue_number,
+ name: 'lifecycle/stale'
+ });
+ console.log(`Removed label 'lifecycle/stale' from issue #${issue_number}`);
+ }
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
deleted file mode 100644
index 3ece9a76b..000000000
--- a/.github/workflows/stale.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
-#
-# You can adjust the behavior by modifying this file.
-# For more information, see:
-# https://github.com/actions/stale
-name: Mark stale issues and pull requests
-
-on:
- schedule:
- - cron: '0 8 * * *'
-
-jobs:
- stale:
-
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
-
- steps:
- - uses: actions/stale@v5
- with:
- repo-token: ${{ secrets.BOT_GITHUB_TOKEN }}
- days-before-stale: 60
- days-before-close: 7
- stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
- stale-pr-message: 'This issue is stale because it has been open 60 days with no activity.'
- close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
- close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity. You can reopen it if you want.'
- stale-pr-label: lifecycle/stale
- stale-issue-label: lifecycle/stale
- exempt-issue-labels: 'openim'
- exempt-pr-labels: 'openim'
- exempt-draft-pr: true
diff --git a/.github/workflows/update-version-file-on-release.yml b/.github/workflows/update-version-file-on-release.yml
new file mode 100644
index 000000000..f582edf48
--- /dev/null
+++ b/.github/workflows/update-version-file-on-release.yml
@@ -0,0 +1,84 @@
+name: Update Version File on Release
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ update-version:
+ runs-on: ubuntu-latest
+ env:
+ TAG_VERSION: ${{ github.event.release.tag_name }}
+ steps:
+ # Step 1: Checkout the original repository's code
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ # Step 2: Set up Git with official account
+ - name: Set up Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ # Step 3: Check and delete existing tag
+ - name: Check and delete existing tag
+ run: |
+ if git rev-parse ${{ env.TAG_VERSION }} >/dev/null 2>&1; then
+ git tag -d ${{ env.TAG_VERSION }}
+ git push --delete origin ${{ env.TAG_VERSION }}
+ fi
+
+ # Step 4: Update version file
+ - name: Update version file
+ run: |
+ echo "${{ env.TAG_VERSION }}" > version/version
+
+ # Step 5: Commit and push changes
+ - name: Commit and push changes
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ git add version/version
+ git commit -m "Update version to ${{ env.TAG_VERSION }}"
+ git push origin HEAD:${{ github.ref }}
+
+ # Step 6: Create and push tag
+ - name: Create and push tag
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ git tag ${{ env.TAG_VERSION }}
+ git push origin ${{ env.TAG_VERSION }}
+
+ # Step 8: Find and Publish Draft Release
+ - name: Find and Publish Draft Release
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ // Get the list of releases
+ const releases = await github.rest.repos.listReleases({
+ owner: context.repo.owner,
+ repo: context.repo.repo
+ });
+
+ // Find the draft release where the title and tag_name are the same
+ const draftRelease = releases.data.find(release =>
+ release.draft && release.name === release.tag_name
+ );
+
+ if (draftRelease) {
+ // Publish the draft release using the release_id
+ await github.rest.repos.updateRelease({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ release_id: draftRelease.id, // Use release_id
+ draft: false
+ });
+
+ core.info(`Draft Release ${draftRelease.tag_name} published successfully.`);
+ } else {
+ core.info("No matching draft release found.");
+ }
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e888e1f25..d86809e7b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ components
logs
out-test
*.db
+open-im-sdk-core.*
### Backup ###
*.bak
@@ -55,7 +56,6 @@ deploy/Open-IM-SDK-Core
# Dependency directories (remove the comment below to include it)
vendor/
bin/
-tools/
tmp/
@@ -169,4 +169,7 @@ fabric.properties
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
-.idea/codestream.xml
\ No newline at end of file
+.idea/codestream.xml
+
+# Mac Desktop Services Store
+.DS_Store
\ No newline at end of file
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index 9fa65aefb..142dd6237 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -1,17 +1,3 @@
-# Copyright © 2023 OpenIM open source community. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
@@ -22,7 +8,6 @@ before:
- go generate ./...
-
git:
# What should be used to sort tags when gathering the current and previous
# tags if there are more than one tag in the same commit.
@@ -49,20 +34,9 @@ git:
report_sizes: true
builds:
- - binary: openim-sdk-core
- id: openim-sdk-core
- main: ./cmd/main.go
- goos:
- - linux
- goarch:
- - amd64
- - arm64
- goarm:
- - "6"
- - "7"
- id: openIM.wasm
- main: wasm/cmd/main.go # 指定 wasm 主文件路径
- binary: openIM.wasm
+ main: wasm/cmd/main.go # Specify the path to the main WASM file
+ binary: openIM
ldflags: "-s -w"
goos:
- js
@@ -113,64 +87,54 @@ archives:
- goos: windows
format: zip
-changelog:
- sort: asc
- use: github
- filters:
- exclude:
- - "^test:"
- - "^chore"
- - "merge conflict"
- - Merge pull request
- - Merge remote-tracking branch
- - Merge branch
- - go mod tidy
- groups:
- - title: Dependency updates
- regexp: '^.*?(feat|fix)\(deps\)!?:.+$'
- order: 300
- - title: "New Features"
- regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
- order: 100
- - title: "Security updates"
- regexp: '^.*?sec(\([[:word:]]+\))??!?:.+$'
- order: 150
- - title: "Bug fixes"
- regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
- order: 200
- - title: "Documentation updates"
- regexp: ^.*?doc(\([[:word:]]+\))??!?:.+$
- order: 400
- - title: "Build process updates"
- regexp: ^.*?build(\([[:word:]]+\))??!?:.+$
- order: 400
- - title: Other work
- order: 9999
-
-
-nfpms:
- - id: packages
- builds:
- - openim-sdk-core
- - openIM.wasm
- # Your app's vendor.
- vendor: OpenIMSDK
- homepage: https://github.com/openimsdk/openim-sdk-core
- maintainer: kubbot
- description: |-
- Auto sync github labels
- kubbot && openimbot
- license: Apache-2.0
- formats:
- - apk
- - deb
- - rpm
- - termux.deb # Since: v1.11
- - archlinux # Since: v1.13
- dependencies:
- - git
- recommends:
- - golang
+# changelog:
+# sort: asc
+# use: github
+# filters:
+# exclude:
+# - "^test:"
+# - "^chore"
+# - "merge conflict"
+# - Merge pull request
+# - Merge remote-tracking branch
+# - Merge branch
+# - go mod tidy
+# groups:
+# - title: Dependency updates
+# regexp: '^.*?(feat|fix)\(deps\)!?:.+$'
+# order: 300
+# - title: "New Features"
+# regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
+# order: 100
+# - title: "Security updates"
+# regexp: '^.*?sec(\([[:word:]]+\))??!?:.+$'
+# order: 150
+# - title: "Bug fixes"
+# regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
+# order: 200
+# - title: "Documentation updates"
+# regexp: ^.*?doc(\([[:word:]]+\))??!?:.+$
+# order: 400
+# - title: "Build process updates"
+# regexp: ^.*?build(\([[:word:]]+\))??!?:.+$
+# order: 400
+# - title: Other work
+# order: 9999
+
+
+# nfpms:
+# - id: packages
+# builds:
+# - openim-sdk-core
+# - openIM.wasm
+# # Your app's vendor.
+# vendor: OpenIMSDK
+# homepage: https://github.com/openimsdk/openim-sdk-core
+# license: Apache-2.0
+# dependencies:
+# - git
+# recommends:
+# - golang
# The lines beneath this are called `modelines`. See `:help modeline`
@@ -181,43 +145,26 @@ nfpms:
# Default: './dist'
dist: ./_output/dist
-# .goreleaser.yaml
-milestones:
- # You can have multiple milestone configs
- -
- # Repository for the milestone
- # Default is extracted from the origin remote URL
- repo:
- owner: user
- name: repo
-
- # Whether to close the milestone
- close: true
-
- # Fail release on errors, such as missing milestone.
- fail_on_error: false
-
- # Name of the milestone
- #
- # Default: '{{ .Tag }}'
- name_template: "Current Release"
-
-# publishers:
-# - name: "fury.io"
-# ids:
-# - packages
-# dir: "{{ dir .ArtifactPath }}"
-# cmd: |
-# bash -c '
-# if [[ "{{ .Tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
-# curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/{{ .Env.USERNAME }}/
-# else
-# echo "Skipping deployment: Non-production release detected"
-# fi'
-
-checksum:
- name_template: "{{ .ProjectName }}_checksums.txt"
- algorithm: sha256
+# # .goreleaser.yaml
+# milestones:
+# # You can have multiple milestone configs
+# -
+# # Repository for the milestone
+# # Default is extracted from the origin remote URL
+# repo:
+# owner: user
+# name: repo
+
+# # Whether to close the milestone
+# close: true
+
+# # Fail release on errors, such as missing milestone.
+# fail_on_error: false
+
+# # Name of the milestone
+# #
+# # Default: '{{ .Tag }}'
+# name_template: "Current Release"
release:
prerelease: auto
diff --git a/CHANGELOG/.chglog/CHANGELOG.tpl.md b/CHANGELOG/.chglog/CHANGELOG.tpl.md
deleted file mode 100644
index 100a29ed8..000000000
--- a/CHANGELOG/.chglog/CHANGELOG.tpl.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-{{ if .Versions -}}
-
-## [Unreleased]
-
-{{ if .Unreleased.CommitGroups -}}
-{{ range .Unreleased.CommitGroups -}}
-### {{ .Title }}
-{{ range .Commits -}}
-- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
-{{ end }}
-{{ end -}}
-{{ end -}}
-{{ end -}}
-
-{{ range .Versions }}
-
-## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
-{{ range .CommitGroups -}}
-### {{ .Title }}
-{{ range .Commits -}}
-- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
-{{ end }}
-{{ end -}}
-
-{{- if .RevertCommits -}}
-### Reverts
-{{ range .RevertCommits -}}
-- {{ .Revert.Header }}
-{{ end }}
-{{ end -}}
-
-{{- if .MergeCommits -}}
-### Pull Requests
-{{ range .MergeCommits -}}
-- {{ .Header }}
-{{ end }}
-{{ end -}}
-
-{{- if .NoteGroups -}}
-{{ range .NoteGroups -}}
-### {{ .Title }}
-{{ range .Notes }}
-{{ .Body }}
-{{ end }}
-{{ end -}}
-{{ end -}}
-{{ end -}}
-
-{{- if .Versions }}
-[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
-{{ range .Versions -}}
-{{ if .Tag.Previous -}}
-[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
-{{ end -}}
-{{ end -}}
-{{ end -}}
\ No newline at end of file
diff --git a/CHANGELOG/.chglog/config.yml b/CHANGELOG/.chglog/config.yml
deleted file mode 100644
index 290193082..000000000
--- a/CHANGELOG/.chglog/config.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright © 2023 OpenIM SDK. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-bin: git
-style: github
-template: CHANGELOG.tpl.md
-info:
- title: CHANGELOG
- repository_url: https://github.com/openimsdk/openim-sdk-core
-options:
- tag_filter_pattern: '^v'
- sort: "date"
-
- commits:
- filters:
- Type:
- - feat
- - fix
- - perf
- - refactor
- - docs
- - test
- - chore
- - ci
- - build
- sort_by: Scope
-
- commit_groups:
- group_by: Type
- sort_by: Title
- title_order:
- - feat
- - fix
- - perf
- - refactor
- - docs
- - test
- - chore
- - ci
- - build
- title_maps:
- feat: Features
-
- header:
- pattern: ""
- pattern_maps:
- - PropName
-
- issues:
- prefix:
- - #
-
- refs:
- actions:
- - Closes
- - Fixes
-
- merges:
- pattern: "^Merge branch '(\\w+)'$"
- pattern_maps:
- - Source
-
- reverts:
- pattern: "^Revert \"([\\s\\S]*)\"$"
- pattern_maps:
- - Header
-
- notes:
- keywords:
- - BREAKING CHANGE
\ No newline at end of file
diff --git a/CHANGELOG/CHANGELOG-1.0.md b/CHANGELOG/CHANGELOG-1.0.md
deleted file mode 100644
index 52983b359..000000000
--- a/CHANGELOG/CHANGELOG-1.0.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## [v1.0.7] - 2021-12-10
-
-
-## [v1.0.6] - 2021-12-03
-
-
-## [v1.0.5] - 2021-11-25
-
-
-## [v1.0.4] - 2021-11-12
-
-
-## [v1.0.3] - 2021-11-05
-
-
-## [v1.0.2] - 2021-11-04
-
-
-## [v1.0.1] - 2021-11-03
-
-
-## v1.0.0 - 2021-10-28
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.7...HEAD
-[v1.0.7]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.6...v1.0.7
-[v1.0.6]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.5...v1.0.6
-[v1.0.5]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.4...v1.0.5
-[v1.0.4]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.3...v1.0.4
-[v1.0.3]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.2...v1.0.3
-[v1.0.2]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.1...v1.0.2
-[v1.0.1]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.0...v1.0.1
diff --git a/CHANGELOG/CHANGELOG-1.1.md b/CHANGELOG/CHANGELOG-1.1.md
deleted file mode 100644
index e69de29bb..000000000
diff --git a/CHANGELOG/CHANGELOG-2.0.md b/CHANGELOG/CHANGELOG-2.0.md
deleted file mode 100644
index 76bef9093..000000000
--- a/CHANGELOG/CHANGELOG-2.0.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## [v2.3.3] - 2022-09-16
-
-
-## [v2.3.2] - 2022-09-09
-
-
-## [v2.3.0-rc0] - 2022-07-15
-
-
-## [v2.2.0] - 2022-07-01
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.1.0] - 2022-06-17
-
-
-## [v2.0.9] - 2022-05-11
-
-
-## [v2.0.8] - 2022-04-29
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.7] - 2022-04-22
-
-
-## [v2.0.6] - 2022-04-08
-
-
-## [v2.0.5] - 2022-04-01
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.4] - 2022-03-25
-
-
-## [v2.0.3] - 2022-03-18
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.2] - 2022-02-24
-
-
-## [v2.0.1] - 2022-02-24
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-
-## v2.0.0 - 2022-02-23
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.3...HEAD
-[v2.3.3]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.2...v2.3.3
-[v2.3.2]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.0-rc0...v2.3.2
-[v2.3.0-rc0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.2.0...v2.3.0-rc0
-[v2.2.0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.1.0...v2.2.0
-[v2.1.0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.9...v2.1.0
-[v2.0.9]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.8...v2.0.9
-[v2.0.8]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.7...v2.0.8
-[v2.0.7]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.6...v2.0.7
-[v2.0.6]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.5...v2.0.6
-[v2.0.5]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.4...v2.0.5
-[v2.0.4]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.3...v2.0.4
-[v2.0.3]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.2...v2.0.3
-[v2.0.2]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.1...v2.0.2
-[v2.0.1]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.0...v2.0.1
diff --git a/CHANGELOG/CHANGELOG-2.1.md b/CHANGELOG/CHANGELOG-2.1.md
deleted file mode 100644
index 83a4cda9d..000000000
--- a/CHANGELOG/CHANGELOG-2.1.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## v2.1.0 - 2022-06-17
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v2.1.0...HEAD
diff --git a/CHANGELOG/CHANGELOG-2.2.md b/CHANGELOG/CHANGELOG-2.2.md
deleted file mode 100644
index 7cd31b116..000000000
--- a/CHANGELOG/CHANGELOG-2.2.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## v2.2.0 - 2022-07-01
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v2.2.0...HEAD
diff --git a/CHANGELOG/CHANGELOG-2.3.md b/CHANGELOG/CHANGELOG-2.3.md
deleted file mode 100644
index 77833bf47..000000000
--- a/CHANGELOG/CHANGELOG-2.3.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## [v2.3.3] - 2022-09-16
-
-
-## [v2.3.2] - 2022-09-09
-
-
-## v2.3.0-rc0 - 2022-07-15
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.3...HEAD
-[v2.3.3]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.2...v2.3.3
-[v2.3.2]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.0-rc0...v2.3.2
diff --git a/CHANGELOG/CHANGELOG-2.9.md b/CHANGELOG/CHANGELOG-2.9.md
deleted file mode 100644
index 1463f52dc..000000000
--- a/CHANGELOG/CHANGELOG-2.9.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Version logging for OpenIM
-
-
-
-- [Version logging for OpenIM](#version-logging-for-openim)
- - [Unreleased](#unreleased)
- - [v2.9.0+1.839643f - 2023-07-07](#v2901839643f---2023-07-07)
- - [v2.9.0+2.35f07fe - 2023-07-06](#v290235f07fe---2023-07-06)
- - [v2.9.0+1.b5072b1 - 2023-07-05](#v2901b5072b1---2023-07-05)
- - [v2.9.0+3.2667a3a - 2023-07-05](#v29032667a3a---2023-07-05)
- - [v2.9.0+7.04818ca - 2023-07-05](#v290704818ca---2023-07-05)
- - [v2.9.0 - 2023-07-04](#v290---2023-07-04)
- - [Reverts](#reverts)
- - [Pull Requests](#pull-requests)
-
-
-
-
-
-## [Unreleased]
-
-
-
-## [v2.9.0+1.839643f] - 2023-07-07
-
-
-## [v2.9.0+2.35f07fe] - 2023-07-06
-
-
-## [v2.9.0+1.b5072b1] - 2023-07-05
-
-
-## [v2.9.0+3.2667a3a] - 2023-07-05
-
-
-## [v2.9.0+7.04818ca] - 2023-07-05
-
-
-## v2.9.0 - 2023-07-04
-### Reverts
-- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/Open-IM-Server/issues/206))
-
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-[Unreleased]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0+1.839643f...HEAD
-[v2.9.0+1.839643f]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0+2.35f07fe...v2.9.0+1.839643f
-[v2.9.0+2.35f07fe]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0+1.b5072b1...v2.9.0+2.35f07fe
-[v2.9.0+1.b5072b1]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0+3.2667a3a...v2.9.0+1.b5072b1
-[v2.9.0+3.2667a3a]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0+7.04818ca...v2.9.0+3.2667a3a
-[v2.9.0+7.04818ca]: https://github.com/openimsdk/Open-IM-Server/compare/v2.9.0...v2.9.0+7.04818ca
diff --git a/CHANGELOG/CHANGELOG-3.0.md b/CHANGELOG/CHANGELOG-3.0.md
deleted file mode 100644
index 4b1279302..000000000
--- a/CHANGELOG/CHANGELOG-3.0.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Version logging for OpenIM
-
-
-
-
-
-
-## [Unreleased]
-
-
-
-## [v3.0.0-rc.1] - 2023-07-13
-
-
-## [v2.3.3] - 2022-09-16
-
-
-## [v2.3.2] - 2022-09-09
-
-
-## [v2.3.0-rc0] - 2022-07-15
-
-
-## [v2.2.0] - 2022-07-01
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.1.0] - 2022-06-17
-
-
-## [v2.0.9] - 2022-05-11
-
-
-## [v2.0.8] - 2022-04-29
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.7] - 2022-04-22
-
-
-## [v2.0.6] - 2022-04-08
-
-
-## [v2.0.5] - 2022-04-01
-### Pull Requests
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.4] - 2022-03-25
-
-
-## [v2.0.3] - 2022-03-18
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.2] - 2022-02-24
-
-
-## [v2.0.1] - 2022-02-24
-### Pull Requests
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-- Merge branch 'tuoyun'
-
-
-
-## [v2.0.0] - 2022-02-23
-
-
-## [v1.0.7] - 2021-12-10
-
-
-## [v1.0.6] - 2021-12-03
-
-
-## [v1.0.5] - 2021-11-25
-
-
-## [v1.0.4] - 2021-11-12
-
-
-## [v1.0.3] - 2021-11-05
-
-
-## [v1.0.2] - 2021-11-04
-
-
-## [v1.0.1] - 2021-11-03
-
-
-## v1.0.0 - 2021-10-28
-
-[Unreleased]: https://github.com/openimsdk/openim-sdk-core/compare/v3.0.0-rc.1...HEAD
-[v3.0.0-rc.1]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.3...v3.0.0-rc.1
-[v2.3.3]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.2...v2.3.3
-[v2.3.2]: https://github.com/openimsdk/openim-sdk-core/compare/v2.3.0-rc0...v2.3.2
-[v2.3.0-rc0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.2.0...v2.3.0-rc0
-[v2.2.0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.1.0...v2.2.0
-[v2.1.0]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.9...v2.1.0
-[v2.0.9]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.8...v2.0.9
-[v2.0.8]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.7...v2.0.8
-[v2.0.7]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.6...v2.0.7
-[v2.0.6]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.5...v2.0.6
-[v2.0.5]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.4...v2.0.5
-[v2.0.4]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.3...v2.0.4
-[v2.0.3]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.2...v2.0.3
-[v2.0.2]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.1...v2.0.2
-[v2.0.1]: https://github.com/openimsdk/openim-sdk-core/compare/v2.0.0...v2.0.1
-[v2.0.0]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.7...v2.0.0
-[v1.0.7]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.6...v1.0.7
-[v1.0.6]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.5...v1.0.6
-[v1.0.5]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.4...v1.0.5
-[v1.0.4]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.3...v1.0.4
-[v1.0.3]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.2...v1.0.3
-[v1.0.2]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.1...v1.0.2
-[v1.0.1]: https://github.com/openimsdk/openim-sdk-core/compare/v1.0.0...v1.0.1
diff --git a/CHANGELOG/CHANGELOG-3.8.md b/CHANGELOG/CHANGELOG-3.8.md
new file mode 100644
index 000000000..fb444b694
--- /dev/null
+++ b/CHANGELOG/CHANGELOG-3.8.md
@@ -0,0 +1,9 @@
+## [v3.8.3-patch.2](https://github.com/openimsdk/openim-sdk-core/releases/tag/v3.8.3-patch.2) (2025-03-03)
+
+### Bug Fixes
+* fix: add a manually triggered IM message synchronization mechanism to… [#869](https://github.com/openimsdk/openim-sdk-core/pull/869)
+* fix: sync self conversation's avatar when user's info changed. [#871](https://github.com/openimsdk/openim-sdk-core/pull/871)
+* fix: directly deduplicate the messages pulled from the server. [#874](https://github.com/openimsdk/openim-sdk-core/pull/874)
+
+**Full Changelog**: [v3.8.3-patch.1...v3.8.3-patch.2](https://github.com/openimsdk/openim-sdk-core/compare/v3.8.3-patch.1...v3.8.3-patch.2)
+
diff --git a/CHANGELOG/README.md b/CHANGELOG/README.md
new file mode 100644
index 000000000..204194de1
--- /dev/null
+++ b/CHANGELOG/README.md
@@ -0,0 +1,4 @@
+# CHANGELOGs
+
+- [CHANGELOG-3.8.md](./CHANGELOG-3.8.md)
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b42bdbaed..666f2f3d1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,7 +16,6 @@ This document provides guidelines and best practices to help you contribute effe
- [Release version](#Release-version)
- [Contact Us](#Contact-Us)
-
## What we expect of you
We hope that anyone can join OpenIM , even if you are a student, writer, translator
@@ -29,39 +28,38 @@ If you are familiar with [Makefile](./Makefile) , you can easily see the clever
The [Makefile](./Makefile) is for every developer, even if you don't know how to use the Makefile tool, don't worry, we provide two great commands to get you up to speed with the Makefile architecture, `make help` and `make help-all`, it can reduce problems of the developing environment.
-
## Code of Conduct
#### Code and doc contribution
Every action to make project OpenIM better is encouraged. On GitHub, every improvement for OpenIM could be via a [PR](https://github.com/openimsdk/openim-sdk-core/pulls) (short for pull request).
-+ If you find a typo, try to fix it!
-+ If you find a bug, try to fix it!
-+ If you find some redundant codes, try to remove them!
-+ If you find some test cases missing, try to add them!
-+ If you could enhance a feature, please **DO NOT** hesitate!
-+ If you find code implicit, try to add comments to make it clear!
-+ If you find code ugly, try to refactor that!
-+ If you can help to improve documents, it could not be better!
-+ If you find document incorrect, just do it and fix that!
-+ ...
+- If you find a typo, try to fix it!
+- If you find a bug, try to fix it!
+- If you find some redundant codes, try to remove them!
+- If you find some test cases missing, try to add them!
+- If you could enhance a feature, please **DO NOT** hesitate!
+- If you find code implicit, try to add comments to make it clear!
+- If you find code ugly, try to refactor that!
+- If you can help to improve documents, it could not be better!
+- If you find document incorrect, just do it and fix that!
+- ...
#### Where should I start?
-Getting good at GitHub is the first step, we have a [list of labes](https://github.com/openimsdk/openim-sdk-core/labels) and reading some of the [common tags](https://github.com/openimsdk/openim-sdk-core/labels?sort=count-desc) helps us get involved in the community quickly.GitHub allows you to filter out types of issues and pull requests, which helps you discover items in need of triaging. This table includes some predetermined searches for convenience:
+Getting good at GitHub is the first step, we have a [list of labes](https://github.com/openimsdk/openim-sdk-core/labels) and reading some of the [common tags](https://github.com/openimsdk/openim-sdk-core/labels?sort=count-desc) helps us get involved in the community quickly.GitHub allows you to filter out types of issues and pull requests, which helps you discover items in need of triaging. This table includes some predetermined searches for convenience:
-| Search | What it sorts |
-| ------------------------------------------------------------ | ------------------------------------------------------- |
-| [created-asc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc) | Untriaged issues by age |
+| Search | What it sorts |
+| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
+| [created-asc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc) | Untriaged issues by age |
| [needs-triage](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+no%3Alabel) | Issues that need to be assigned to a Labels |
-| [`is:open is:issue`](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+is%3Aissue+sort%3Aupdated-desc) | Newest incoming issues |
-| [comments-desc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acomments-desc) | Busiest untriaged issues, sorted by # of comments |
-| [comments-asc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acomments-asc) | Issues that need more attention, based on # of comments |
+| [`is:open is:issue`](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+is%3Aissue+sort%3Aupdated-desc) | Newest incoming issues |
+| [comments-desc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acomments-desc) | Busiest untriaged issues, sorted by # of comments |
+| [comments-asc](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Acomments-asc) | Issues that need more attention, based on # of comments |
We suggest preparing your triage by filtering out the oldest, unlabelled issues and pull requests first.
-1. If you are new to the project, don't know how to contribute OpenIM, please check out the [good first issue](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label and [help wanted](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+is%3Aissue+label%3A"help+wanted"+sort%3Aupdated-desc).
+1. If you are new to the project, don't know how to contribute OpenIM, please check out the [good first issue](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label and [help wanted](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aopen+is%3Aissue+label%3A"help+wanted"+sort%3Aupdated-desc).
2. You should be good at filtering the OpenIM issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+label%3Akind%2Ffeature+sort%3Aupdated-desc+) proposals, and [bug](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+label%3Akind%2Fbug+sort%3Aupdated-desc+) fixes.
3. If you are looking for something to work on, check out our [open issues](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
4. If you have an idea for a new feature, please [open an issue](https://github.com/openimsdk/openim-sdk-core/issues/new/choose), and we can discuss it.
@@ -69,15 +67,13 @@ We suggest preparing your triage by filtering out the oldest, unlabelled issues
> **Note**
> Reply to `/assign` or `/assign @yourself` with a question you wish to resolve, and we'll assign the question to you and your name will be listed under `Assignees`
-
-
#### Design documents
For any substantial design, there should be a well-crafted design document. This document is not just a simple record, but also a detailed description and manifestation, which can help team members better understand the design thinking and grasp the design direction. In the process of writing the design document, we can choose to use tools such as `Google Docs` or `Notion`, and even mark **RFC** in [issues](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) or [discussions](https://github.com/openimsdk/openim-sdk-core/discussions) for better collaboration. Of course, after completing the design document, we should also add it to our [Shared Drive](https://drive.google.com/drive/) and notify the appropriate working group to let everyone know of its existence. Only by doing so can we maximize the effectiveness of the design document and provide strong support for the smooth progress of the project.
Anybody can access the shared Drive for reading. To get access to comment. Once you've done that, head to the [shared Drive](https://drive.google.com/) and behold all the docs.
-In addition to that, we'd love to invite you to [Join Our Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) where you can play with your imagination, tell us what you're working on, and get a quick response.
+In addition to that, we'd love to invite you to [Join Our Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) where you can play with your imagination, tell us what you're working on, and get a quick response.
When documenting a new design, we recommend a 2-step approach:
@@ -86,19 +82,18 @@ When documenting a new design, we recommend a 2-step approach:
**In order to contribute a feature to OpenIM you'll need to go through the following steps:**
-+ Discuss your idea with the appropriate [working groups](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) on the working group's Slack channel.
-+ Once there is general agreement that the feature is useful, create a GitHub issue to track the discussion. The issue should include information about the requirements and use cases that it is trying to address.
-+ Include a discussion of the proposed design and technical details of the implementation in the issue.
+- Discuss your idea with the appropriate [working groups](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) on the working group's Slack channel.
+- Once there is general agreement that the feature is useful, create a GitHub issue to track the discussion. The issue should include information about the requirements and use cases that it is trying to address.
+- Include a discussion of the proposed design and technical details of the implementation in the issue.
But keep in mind that there is no guarantee of it being accepted and so it is usually best to get agreement on the **idea/design** before time is spent coding it. However, sometimes seeing the exact code change can help focus discussions, so the choice is up to you.
-
-
## Getting Started
+
> **Note**
->
+>
> A simple example allows you to quickly contribute your first **PR** to OpenIM.
-To propose PR for the OpenIM item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:
+> To propose PR for the OpenIM item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:
1. **Fork** the repository(OpenIM)
@@ -131,13 +126,13 @@ To propose PR for the OpenIM item, we assume you have registered a GitHub ID. Th
❯ git checkout upstream/main
```
- > **Note**
+ > **Note**
>
> Please don't use `git pull` instead of the above `fetch` and `rebase`. Since `git pull` executes a merge, it creates merge commits. These make the commit history messy and violate the principle that commits ought to be individually understandable and useful.
>
> You might also consider changing your `.git/config` file via `git config branch.autoSetupRebase always` to change the behavior of `git pull`, or another non-merge option such as `git pull --rebase`.
- Create a new branch:
+ Create a new branch:
```bash
❯ git checkout -b
@@ -163,11 +158,11 @@ To propose PR for the OpenIM item, we assume you have registered a GitHub ID. Th
❯ git rebase -i # rebase with interactive mode to `squash` your commits into a single one
❯ git push # push to the remote repository, if it's a first time push, run git push --set-upstream origin
```
-
+
You can also use `git commit -s --amend && git push -f` to update modifications on the previous commit.
-
+
If you have developed multiple features in the same branch, you should create PR separately by rebasing to the main branch between each push:
-
+
```bash
# create new branch, for example git checkout -b feature/infra
❯ git checkout -b
@@ -182,13 +177,11 @@ To propose PR for the OpenIM item, we assume you have registered a GitHub ID. Th
❯ git commit -m -s "feat: feature two"
# then create pull request, and merge
```
-
+
7. **Open a pull request** to `OpenIMSDK/openim-sdk-core:main`
It is recommended to review your changes before filing a pull request. Check if your code doesn't conflict with the main branch and no redundant code is included.
-
-
## Style and Specification
We divide the problem into security and general problems:
@@ -209,21 +202,21 @@ To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](
**There are a lot of cases when you could open an issue:**
-+ bug report
-+ feature request
-+ OpenIM performance issues
-+ feature proposal
-+ feature design
-+ help wanted
-+ doc incomplete
-+ test improvement
-+ any questions on OpenIM project
-+ and so on
+- bug report
+- feature request
+- OpenIM performance issues
+- feature proposal
+- feature design
+- help wanted
+- doc incomplete
+- test improvement
+- any questions on OpenIM project
+- and so on
Also, we must be reminded when submitting a new question about OpenIM, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on.
> **Note**
-> We have requirements for **Commits**, **PR**, **Docs**, and good standards help us collaborate better and understand what you're doing.
+> We have requirements for **Commits**, **PR**, **Docs**, and good standards help us collaborate better and understand what you're doing.
#### Commit Rules
@@ -235,20 +228,20 @@ Commit message could help reviewers better understand what the purpose of submit
We use [Semantic Commits](https://www.conventionalcommits.org/en/v1.0.0/) to make it easier to understand what a commit does and to build pretty changelogs. Please use the following prefixes for your commits:
-+ `docs: xxxx`. For example, "docs: add docs about storage installation".
-+ `feature: xxxx`.For example, "feature: make result show in sorted order".
-+ `bugfix: xxxx`. For example, "bugfix: fix panic when input nil parameter".
-+ `style: xxxx`. For example, "style: format the code style of Constants.java".
-+ `refactor: xxxx.` For example, "refactor: simplify to make codes more readable".
-+ `test: xxx`. For example, "test: add unit test case for func InsertIntoArray".
-+ `chore: xxx.` For example, "chore: integrate travis-ci". It's the type of mantainance change.
-+ other readable and explicit expression ways.
+- `docs: xxxx`. For example, "docs: add docs about storage installation".
+- `feature: xxxx`.For example, "feature: make result show in sorted order".
+- `bugfix: xxxx`. For example, "bugfix: fix panic when input nil parameter".
+- `style: xxxx`. For example, "style: format the code style of Constants.java".
+- `refactor: xxxx.` For example, "refactor: simplify to make codes more readable".
+- `test: xxx`. For example, "test: add unit test case for func InsertIntoArray".
+- `chore: xxx.` For example, "chore: integrate travis-ci". It's the type of mantainance change.
+- other readable and explicit expression ways.
On the other side, we discourage contributors from committing message like the following ways:
-+ ~~fix bug~~
-+ ~~update~~
-+ ~~add doc~~
+- ~~fix bug~~
+- ~~update~~
+- ~~add doc~~
**🥈 Commit Content:**
@@ -276,22 +269,22 @@ You can find some very formal PR in [RFC](https://github.com/openimsdk/openim-sd
**📖 Opening PRs:**
-+ As long as you are working on your **PR**, please mark it as a draft.
-+ Please make sure that your **PR** is up-to-date with the latest changes in `main`
-+ Mention the issue that your **PR** is addressing. For example, `Fixes: #{ID_1}, #{ID_2}`
-+ Make sure that your **PR** passes all checks.
+- As long as you are working on your **PR**, please mark it as a draft.
+- Please make sure that your **PR** is up-to-date with the latest changes in `main`
+- Mention the issue that your **PR** is addressing. For example, `Fixes: #{ID_1}, #{ID_2}`
+- Make sure that your **PR** passes all checks.
**🈴 Reviewing PRs:**
-+ Be respectful and constructive.
-+ Assign yourself to the **PR**.
-+ Check if all checks are passing.
-+ Suggest changes instead of simply commenting on found issues.
-+ If you are unsure about something, ask the author.
-+ If you are not sure if the changes work, try them out.
-+ Reach out to other reviewers if you are unsure about something.
-+ If you are happy with the changes, approve the **PR**.
-+ Merge the **PR** once it has all approvals and the checks are passing.
+- Be respectful and constructive.
+- Assign yourself to the **PR**.
+- Check if all checks are passing.
+- Suggest changes instead of simply commenting on found issues.
+- If you are unsure about something, ask the author.
+- If you are not sure if the changes work, try them out.
+- Reach out to other reviewers if you are unsure about something.
+- If you are happy with the changes, approve the **PR**.
+- Merge the **PR** once it has all approvals and the checks are passing.
**⚠️ DCO check:**
@@ -305,7 +298,7 @@ To sign off the last commit you made, you can use:
❯ git commit --amend --signoff
```
-Contributors *sign-off* that they adhere to these requirements by adding a `Signed-off-by` line to commit messages.
+Contributors _sign-off_ that they adhere to these requirements by adding a `Signed-off-by` line to commit messages.
Git even has a `-s` command line option to append this automatically to your commit message:
@@ -327,7 +320,6 @@ git() {
}
```
-
#### CI actions
We host CI on [GitHub Actions](https://github.com/openimsdk/openim-sdk-core/actions), we will make sure PR pass tests before we can merge it.
@@ -337,15 +329,13 @@ These two kind of tests: `lint` and `unit test`
`lint` tests if your code matches our code conventions, please consult [golangci-lint](https://golangci-lint.run/) and [lint config](https://github.com/openimsdk/openim-sdk-core/blob/main/.golangci.yml)
> **Note**
->
-> You can use the [Makefile](./Makefile) to run Lint with the command `make lint`.
-
+>
+> You can use the [Makefile](./Makefile) to run Lint with the command `make lint`.
`unit test` runs all the test in code, and the code coverage should not less than 60 percent, record us in [codeclimate](https://codeclimate.com/github/OpenIMSDK/openim-sdk-core) OpenIM the unit test coverage data.
-
> **Note**
->
+>
> We use the [Makefile](./Makefile) utility, `make tese` to run the unit tests, and the `make cover` utility to check the unit test coverage.
Try your best to keep every function has been tested, it keeps the function behaves as intended.
@@ -354,10 +344,10 @@ Try your best to keep every function has been tested, it keeps the function beha
**The documentation for OpenIM includes:**
-+ [README.md](https://github.com/openimsdk/openim-sdk-core/blob/main/README.md): This file includes the basic information and instructions for getting started with OpenIM.
-+ [README_zh-CN.md](https://github.com/openimsdk/openim-sdk-core/blob/main/README_zh-CN.md): This file includes the basic information and instructions for getting started with OpenIM in Chinese.
-+ [CONTRIBUTING.md](https://github.com/openimsdk/openim-sdk-core/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to OpenIM's codebase, such as how to submit issues, pull requests, and code reviews.
-+ [Official Documentation](https://doc.rentsoft.cn): This is the official documentation for OpenIM, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips.
+- [README.md](https://github.com/openimsdk/openim-sdk-core/blob/main/README.md): This file includes the basic information and instructions for getting started with OpenIM.
+- [README_zh-CN.md](https://github.com/openimsdk/openim-sdk-core/blob/main/README_zh-CN.md): This file includes the basic information and instructions for getting started with OpenIM in Chinese.
+- [CONTRIBUTING.md](https://github.com/openimsdk/openim-sdk-core/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to OpenIM's codebase, such as how to submit issues, pull requests, and code reviews.
+- [Official Documentation](https://doc.rentsoft.cn): This is the official documentation for OpenIM, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips.
**Please obey the following rules to better format the docs, which would greatly improve the reading experience.**
@@ -369,42 +359,38 @@ Try your best to keep every function has been tested, it keeps the function beha
6. Please check if there's any typos in the docs before submitting PRs.
**Markfile Lint:**
-We integrated in the CICD actions [markdownlint](https://github.com/markdownlint/markdownlint), it detects Markfile specification.
-> **Note**
-> We recommend reading [markdownlint rules](https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md), This document contains a description of all rules, what they are checking for, as well as an examples of documents that break the rule and corrected versions of the examples.
-
-
+We integrated in the CICD actions [markdownlint](https://github.com/markdownlint/markdownlint), it detects Markfile specification.
+> **Note**
+> We recommend reading [markdownlint rules](https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md), This document contains a description of all rules, what they are checking for, as well as an examples of documents that break the rule and corrected versions of the examples.
## Engage to help anything
We choose GitHub as the primary place for OpenIM to collaborate. So the latest updates of OpenIM are always here. Although contributions via **PR** is an explicit way to help, we still call for any other ways.
-+ reply to other's [issues](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) if you could;
-+ help solve other user's problems;
-+ help review other's [PR](https://github.com/openimsdk/openim-sdk-core/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design;
-+ discuss about OpenIM to make things clearer;
-+ advocate [OpenIM](https://google.com/search?q=OpenIM) technology beyond GitHub;
-+ write blogs on OpenIM and so on.
+- reply to other's [issues](https://github.com/openimsdk/openim-sdk-core/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) if you could;
+- help solve other user's problems;
+- help review other's [PR](https://github.com/openimsdk/openim-sdk-core/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design;
+- discuss about OpenIM to make things clearer;
+- advocate [OpenIM](https://google.com/search?q=OpenIM) technology beyond GitHub;
+- write blogs on OpenIM and so on.
In a word, **ANY HELP IS CONTRIBUTION.**
-
-
## Release version
Releases of OpenIM are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this:
🎯 **A PR is merged to the `main` branch:**
-+ Release please is triggered, creates or updates a new release PR
-+ This is done with every merge to main, the current release PR is updated every time
+- Release please is triggered, creates or updates a new release PR
+- This is done with every merge to main, the current release PR is updated every time
🎯 **Merging the 'release please' PR to `main`:**
-+ Release please is triggered, creates a new release and updates the changelog based on the commit messages
-+ GoReleaser is triggered, builds the binaries and attaches them to the release
-+ Containers are created and pushed to the container registry
+- Release please is triggered, creates a new release and updates the changelog based on the commit messages
+- GoReleaser is triggered, builds the binaries and attaches them to the release
+- Containers are created and pushed to the container registry
With the next relevant merge, a new release PR will be created and the process starts again
@@ -412,24 +398,23 @@ With the next relevant merge, a new release PR will be created and the process s
If you want to manually set the version, you can create a PR with an empty commit message that contains the version number in the commit message. For example:
-Such a commit can get produced as follows:
+Such a commit can get produced as follows:
-````bash
+```bash
❯ git commit --allow-empty -m "chore: release 0.0.3" -m "Release-As: 0.0.3
-````
-
+```
## Contact Us
We value close connections with our users, developers, and contributors here at openim-sdk-core. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
-Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of openim-sdk-core. You can ask technical questions, seek help, or share your experiences with other users of openim-sdk-core.
+Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of openim-sdk-core. You can ask technical questions, seek help, or share your experiences with other users of openim-sdk-core.
In addition to Slack, we also offer the following ways to get in touch:
-+
We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 openim-sdk-core slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) team channel.
-+
Get in touch with us on [Gmail](https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=winxu81@gmail.com). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
-+
Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with openim-sdk-core projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
-+
Add [Wechat](https://github.com/openimsdk/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of openim-sdk-core. We will process your request as soon as possible.
+-
We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 openim-sdk-core slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) team channel.
+-
Get in touch with us on [Gmail](https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=winxu81@gmail.com). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
+-
Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with openim-sdk-core projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
+-
Add [Wechat](https://github.com/openimsdk/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of openim-sdk-core. We will process your request as soon as possible.
Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
diff --git a/LICENSE b/LICENSE
index f49a4e16e..4591ca426 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,35 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+# Open Source License
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+OpenIM is licensed under the Apache License 2.0, with the following additional conditions:
- 1. Definitions.
+1. OpenIM may be utilized commercially, including as a backend service for other applications or as an application development platform for enterprises.
+A commercial license must be obtained from the producer if:
+ - Under no circumstances may you operate a multi-tenant or multi-business environment using the OpenIM source code, whether or not you have modified the repository code. In other words, a single instance of OpenIM may not simultaneously serve multiple enterprises, nor may it serve multiple lines of business within the same enterprise.
+ - If you intend to operate in such a multi-tenant or multi-business manner, you must obtain a commercial license from the producer in advance.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
+2. As a contributor, you should agree that:
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
+ a. The producer can adjust the open-source agreement to be more strict or more relaxed as deemed necessary.
+ b. Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
+Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
+For any licensing-related questions or to obtain a commercial license, please contact contact@openim.io.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
+© 2024 OpenIMSDK
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
+----------
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
+ http://www.apache.org/licenses/LICENSE-2.0
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/Makefile b/Makefile
index cdaf84c4e..4105ab18e 100644
--- a/Makefile
+++ b/Makefile
@@ -4,10 +4,6 @@
ROOT_PACKAGE=github.com/openimsdk/Open-IM-SDK-Core
-# Copyright 2023 OpenIM. All rights reserved.
-# Use of this source code is governed by a MIT style
-# license that can be found in the LICENSE file.
-
###################################=> common commands <=#############################################
# ========================== Capture Environment ===============================
# get the repo root and output path
@@ -77,10 +73,9 @@ MAKEFLAGS += --no-print-directory
endif
# The OS must be linux when building docker images
-# !WARNING: linux_mips64 linux_mips64le
-PLATFORMS ?= linux_s390x darwin_amd64 windows_amd64 linux_amd64 linux_arm64 linux_ppc64le
+
# The OS can be linux/windows/darwin when building binaries
-# PLATFORMS ?= darwin_amd64 windows_amd64 linux_amd64 linux_arm64
+PLATFORMS ?= darwin_amd64 darwin_arm64 windows_amd64 linux_amd64 linux_arm64
# Set a specific PLATFORM
ifeq ($(origin PLATFORM), undefined)
@@ -99,27 +94,11 @@ else
IMAGE_PLAT := $(PLATFORM)
endif
-# Copy githook scripts when execute makefile
-# NEED Remove. DON'T REJECT!!
-# TODO! GIT_FILE_SIZE_LIMIT=42000000 git commit -m "This commit is allowed file sizes up to 42MB"
-# COPY_GITHOOK:=$(shell cp -f scripts/githooks/* .git/hooks/; chmod +x .git/hooks/*)
# Linux command settings
FIND := find . ! -path './image/*' ! -path './vendor/*' ! -path './bin/*'
XARGS := xargs -r
-# ==============================================================================
-# TODO: License selection
-# LICENSE_TEMPLATE ?= $(ROOT_DIR)/scripts/LICENSE/license_templates.txt # MIT License
-LICENSE_TEMPLATE ?= $(ROOT_DIR)/scripts/LICENSE/LICENSE_TEMPLATES # Apache License
-
-# COMMA: Concatenate multiple strings to form a list of strings
-COMMA := ,
-# SPACE: Used to separate strings
-SPACE :=
-# SPACE: Replace multiple consecutive Spaces with a single space
-SPACE +=
-
# ==============================================================================
# Build definition
@@ -147,16 +126,6 @@ ifeq ($(origin GOBIN), undefined)
GOBIN := $(GOPATH)/bin
endif
-COMMANDS ?= $(filter-out %.md, $(wildcard ${ROOT_DIR}/cmd/*))
-BINS ?= $(foreach cmd,${COMMANDS},$(notdir ${cmd}))
-
-ifeq (${COMMANDS},)
- $(error Could not determine COMMANDS, set ROOT_DIR or run in source dir)
-endif
-ifeq (${BINS},)
- $(error Could not determine BINS, set ROOT_DIR or run in source dir)
-endif
-
EXCLUDE_TESTS=github.com/openimsdk/openim-sdk-core/test
# ==============================================================================
@@ -164,7 +133,7 @@ EXCLUDE_TESTS=github.com/openimsdk/openim-sdk-core/test
## all: Build all the necessary targets.
.PHONY: all
-all: copyright-verify build # tidy lint cover
+all: build # tidy lint cover
# Define available OS and ARCH
OSES = linux
@@ -301,20 +270,6 @@ docker-push:
docker-buildx-push:
docker buildx build --platform linux/arm64,linux/amd64 -t ${IMG} . --push
-## copyright-verify: Validate boilerplate headers for assign files.
-.PHONY: copyright-verify
-copyright-verify: tools.verify.addlicense copyright-add
- @echo "===========> Validate boilerplate headers for assign files starting in the $(ROOT_DIR) directory"
- @$(TOOLS_DIR)/addlicense -v -check -ignore **/test/** -f $(LICENSE_TEMPLATE) $(CODE_DIRS)
- @echo "===========> End of boilerplate headers check..."
-
-## copyright-add: Add the boilerplate headers for all files.
-.PHONY: copyright-add
-copyright-add: tools.verify.addlicense
- @echo "===========> Adding $(LICENSE_TEMPLATE) the boilerplate headers for all files"
- @$(TOOLS_DIR)/addlicense -y $(shell date +"%Y") -v -c "OpenIM open source community." -f $(LICENSE_TEMPLATE) $(CODE_DIRS)
- @echo "===========> End the copyright is added..."
-
## clean: Clean all builds.
.PHONY: clean
clean:
@@ -331,7 +286,7 @@ help: Makefile
######################################=> common tools<= ############################################
# tools
-BUILD_TOOLS ?= go-gitlint golangci-lint goimports addlicense deepcopy-gen conversion-gen ginkgo go-junit-report
+BUILD_TOOLS ?= go-gitlint golangci-lint goimports addlicense deepcopy-gen conversion-gen ginkgo
## tools.verify.%: Check if a tool is installed and install it
.PHONY: tools.verify.%
@@ -374,16 +329,6 @@ install.conversion-gen:
install.ginkgo:
@$(GO) install github.com/onsi/ginkgo/ginkgo@v1.16.2
-.PHONY: install.go-gitlint
-# wget -P _output/tools/ https://openim-1306374445.cos.ap-guangzhou.myqcloud.com/openim/tools/go-gitlint
-# go install github.com/antham/go-gitlint/cmd/gitlint@latest
-install.go-gitlint:
- @wget -q https://openim-1306374445.cos.ap-guangzhou.myqcloud.com/openim/tools/go-gitlint -O ${TOOLS_DIR}/go-gitlint
- @chmod +x ${TOOLS_DIR}/go-gitlint
-
-.PHONY: install.go-junit-report
-install.go-junit-report:
- @$(GO) install github.com/jstemmer/go-junit-report@latest
# ==============================================================================
# Tools that might be used include go gvm, cos
diff --git a/README.md b/README.md
index c04d17a95..1402cdead 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,17 @@
- ⭐️ Used in IOS, Android, PC and other platforms ⭐️
+ ⭐️ Used in iOS, Android, PC, Web (WebAssembly) and other platforms ⭐️
-
+
@@ -25,63 +25,98 @@
-----
+---
-## 🧩 Awesome features
+## 🧩 Features
-OpenIM-SDK-core is a core SDK of OpenIM.
+
-1. Manage WebSocket long connections, responsible for creating, closing and reconnecting connections.
-2. Encoding and decoding. Encode and decode messages in binary format to achieve cross-language compatibility.
-3. Implement basic protocols of OpenIM, such as login, push, etc.
-4. Provide an event handling mechanism to convert received messages into corresponding events and pass them to upper layer applications for processing.
-5. Cache management. Manage user, group, blacklists, and other cache information.
-6. Provide basic IM function APIs such as sending messages, creating groups, etc. Hide the underlying implementation details from the upper layer application.
+OpenIM-SDK-core is the core SDK of OpenIM, serving as the cross-platform foundation for all open-source OpenIM SDKs (excluding mini web).
+All open-source OpenIM SDKs (except mini web) are built upon this core layer, ensuring consistency, stability, and seamless cross-platform integration.
+
+
+- [x] Network management with intelligent heartbeat
+- [x] Message encoding and decoding
+- [x] Local message storage
+- [x] Relationship data synchronization
+- [x] IM message synchronization
+- [x] Cross-platform communication and callback management
+
+- Supported Platforms
+ - [x] Windows
+ - [x] MacOS
+ - [x] Linux
+ - [x] iOS
+ - [x] Android
+ - [x] Web (WebAssembly)
+ - [ ] Mini Web
## Quickstart
-> **Note**: You can get started quickly with openim-sdk-core.
+> **Note**: This section guides you on how to quickly connect to the server and get OpenIM-SDK-core running.
-
- Work with Makefile
+### 🚀 Connect to the Server and Run
-```bash
-❯ make help # show help
-❯ make build # build binary
-```
+Follow these steps to quickly set up and run OpenIM-SDK-core by simulating an app environment using test files.
-
-
- Work with actions
+1. **Enter the `test` directory**
+ ```bash
+ # This folder contains unit test files for all interface functions of OpenIM-SDK-core,
+ # used to simulate an app connecting to the server for login testing.
+ cd test
+ ```
+2. **Modify the configuration file**
+ > [Set up your own server beforehand.](https://github.com/openimsdk/open-im-server.git)
-Actions provide handling of PR and issue.
-We used the bot @kubbot, It can detect issues in Chinese and translate them to English, and you can interact with it using the command `/comment`.
+- Open the config file in the test directory.
+- Update the following fields with your server information:
-Comment in an issue:
+ ```json
+ {
+ "APIADDR": "http://your-server-api-address",
+ "WSADDR": "ws://your-server-websocket-address",
+ "UserID": "your-test-user-id"
+ }
+ ```
-```bash
-❯ /intive
-```
+3. **Run test functions to simulate an app using the SDK**
+
+- Identify the test function you want to execute (The `init` file has already completed the SDK initialization and login logic. You can now call other functions).
+ ```bash
+ go test -run TestFunctionName
+ ```
+- Example: Running the login test
+ ```bash
+ go test -run Test_GetAllConversationList
+ ```
+ Now, you can use the test cases to simulate real SDK usage, just like an actual app.
+
+## 📦 Build and Package for Different Platforms
+
+Once the SDK is tested successfully, you can build and package it for various platforms:
+
+- **Android/iOS**
+
+Refer to [this guide](./docs/gomobile-android-ios-setup-cn.md) for detailed instructions on building and packaging for Android and iOS.
+
+- **WebAssembly**
-
-
- Work with Tools
+Navigate to the `wasm/cmd` directory and run the following command to build the WebAssembly package:
```bash
-❯ make tools
+make wasm # Ensure Go is installed
```
-
-
- Work with Docker
+If you are on Windows, use the following command instead:
```bash
-$ make deploy
+mingw32-make wasm # Ensure MinGW64 is installed
```
-
+- **Windows, MacOS, Linux**
+Refer to [this repository](https://github.com/openimsdk/openim-sdk-cpp.git) for platform-specific build instructions.
## Contributing & Development
@@ -101,31 +136,31 @@ openim-sdk-core maintains a [public roadmap](https://github.com/openimsdk/commun
### common
-+ https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
-+ https://github.com/openimsdk/community: Community Management for OpenIM
+- https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
+- https://github.com/openimsdk/community: Community Management for OpenIM
### OpenIM **Links**
Contains some common parts of the OpenIM community.
-+ https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
-+ https://github.com/openimsdk/openim-sdk-core: The IMSDK implemented by golang can be used in IOS, Android, PC and other platforms.
-+ https://github.com/openimsdk/openim-sdk-core: Instant messaging IM server.
-+ https://github.com/openimsdk/community: Community Management for OpenIM.
+- https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
+- https://github.com/openimsdk/openim-sdk-core: The IMSDK implemented by golang can be used in iOS, Android, PC and other platforms.
+- https://github.com/openimsdk/openim-sdk-core: Instant messaging IM server.
+- https://github.com/openimsdk/community: Community Management for OpenIM.
### SDKs
-+ [openim-sdk-core](https://github.com/openimsdk/openim-sdk-core): A cross-platform SDK implemented in golang that can be used in IOS, Android, PC, and other platforms.
-+ [Open-IM-SDK-iOS](https://github.com/openimsdk/Open-IM-SDK-iOS): An iOS SDK generated based on openim-sdk-core, available for developers to reference.
-+ [Open-IM-SDK-Android](https://github.com/openimsdk/Open-IM-SDK-Android): An Android SDK generated based on openim-sdk-core, available for developers to reference.
-+ [Open-IM-SDK-Flutter](https://github.com/openimsdk/Open-IM-SDK-Flutter): A Flutter SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
-+ [Open-IM-SDK-Uniapp](https://github.com/openimsdk/Open-IM-SDK-Uniapp): A uni-app SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
+- [openim-sdk-core](https://github.com/openimsdk/openim-sdk-core): A cross-platform SDK implemented in golang that can be used in iOS, Android, PC, and other platforms.
+- [Open-IM-SDK-iOS](https://github.com/openimsdk/Open-IM-SDK-iOS): An iOS SDK generated based on openim-sdk-core, available for developers to reference.
+- [Open-IM-SDK-Android](https://github.com/openimsdk/Open-IM-SDK-Android): An Android SDK generated based on openim-sdk-core, available for developers to reference.
+- [Open-IM-SDK-Flutter](https://github.com/openimsdk/Open-IM-SDK-Flutter): A Flutter SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
+- [Open-IM-SDK-Uniapp](https://github.com/openimsdk/Open-IM-SDK-Uniapp): A uni-app SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
### Demos
-+ [Open-IM-iOS-Demo](https://github.com/openimsdk/Open-IM-iOS-Demo): An iOS demo based on Open-IM-SDK-iOS, available for developers to reference.
-+ [Open-IM-Android-Demo](https://github.com/openimsdk/Open-IM-Android-Demo): An Android demo based on Open-IM-SDK-Android, available for developers to reference.
-+ [Open-IM-Flutter-Demo](https://github.com/openimsdk/Open-IM-Flutter-Demo): A Flutter demo based on Open-IM-SDK-Flutter, available for developers to reference.
+- [Open-IM-iOS-Demo](https://github.com/openimsdk/Open-IM-iOS-Demo): An iOS demo based on Open-IM-SDK-iOS, available for developers to reference.
+- [Open-IM-Android-Demo](https://github.com/openimsdk/Open-IM-Android-Demo): An Android demo based on Open-IM-SDK-Android, available for developers to reference.
+- [Open-IM-Flutter-Demo](https://github.com/openimsdk/Open-IM-Flutter-Demo): A Flutter demo based on Open-IM-SDK-Flutter, available for developers to reference.
## Used By
@@ -135,7 +170,7 @@ Please leave your use cases in the comments [here](https://github.com/openimsdk/
## License
-[openim-sdk-core](https://github.com/openimsdk/openim-sdk-core) is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/openimsdk/openim-sdk-core/tree/main/LICENSE) for the full license text.
+For more details, please refer to [here](./LICENSE).
## Thanks to our contributors!
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 3c7a22db4..85e6192d1 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -1,17 +1,17 @@
- ⭐️ Used in IOS, Android, PC and other platforms ⭐️
+ ⭐️ 适用于 iOS、Android、PC、Web(WebAssembly)及其他平台 ⭐️
-
+
@@ -24,3 +24,156 @@
+
+---
+
+## 🧩 Features
+
+
+
+OpenIM-SDK-core is the core SDK of OpenIM, serving as the cross-platform foundation for all open-source OpenIM SDKs (excluding mini web).
+All open-source OpenIM SDKs (except mini web) are built upon this core layer, ensuring consistency, stability, and seamless cross-platform integration.
+
+
+
+- [x] Network management with intelligent heartbeat
+- [x] Message encoding and decoding
+- [x] Local message storage
+- [x] Relationship data synchronization
+- [x] IM message synchronization
+- [x] Cross-platform communication and callback management
+
+- Supported Platforms
+ - [x] Windows
+ - [x] MacOS
+ - [x] Linux
+ - [x] iOS
+ - [x] Android
+ - [x] Web (WebAssembly)
+ - [ ] Mini Web
+
+## Quickstart
+
+> **Note**: This section guides you on how to quickly connect to the server and get OpenIM-SDK-core running.
+
+### 🚀 Connect to the Server and Run
+
+Follow these steps to quickly set up and run OpenIM-SDK-core by simulating an app environment using test files.
+
+1. **Enter the `test` directory**
+ ```bash
+ # This folder contains unit test files for all interface functions of OpenIM-SDK-core,
+ # used to simulate an app connecting to the server for login testing.
+ cd test
+ ```
+2. **Modify the configuration file**
+ > [Set up your own server beforehand.](https://github.com/openimsdk/open-im-server.git)
+
+- Open the config file in the test directory.
+- Update the following fields with your server information:
+
+ ```json
+ {
+ "APIADDR": "http://your-server-api-address",
+ "WSADDR": "ws://your-server-websocket-address",
+ "UserID": "your-test-user-id"
+ }
+ ```
+
+3. **Run test functions to simulate an app using the SDK**
+
+- Identify the test function you want to execute (The `init` file has already completed the SDK initialization and login logic. You can now call other functions).
+ ```bash
+ go test -run TestFunctionName
+ ```
+- Example: Running the login test
+ `bash
+ go test -run Test_GetAllConversationList
+ `
+ Now, you can use the test cases to simulate real SDK usage, just like an actual app.
+
+## 📦 Build and Package for Different Platforms
+
+Once the SDK is tested successfully, you can build and package it for various platforms:
+
+- **Android/iOS**
+
+Refer to [this guide](./docs/CHANGELOG.md) for detailed instructions on building and packaging for Android and iOS.
+
+- **WebAssembly**
+
+Navigate to the `wasm/cmd` directory and run the following command to build the WebAssembly package:
+
+```bash
+make wasm # Ensure Go is installed
+```
+
+If you are on Windows, use the following command instead:
+
+```bash
+mingw32-make wasm # Ensure MinGW64 is installed
+```
+
+- **Windows, MacOS, Linux**
+
+Refer to [this repository](https://github.com/openimsdk/openim-sdk-cpp.git) for platform-specific build instructions.
+
+## Contributing & Development
+
+OpenIM Our goal is to build a top-level open source community. We have a set of standards, in the [Community repository](https://github.com/openimsdk/community).
+
+If you'd like to contribute to this openim-sdk-core repository, please read our [contributor documentation](https://github.com/openimsdk/openim-sdk-core/blob/main/CONTRIBUTING.md).
+
+## community meeting
+
+We welcome everyone to join us and contribute to openim-sdk-core, whether you are new to open source or professional. We are committed to promoting an open source culture, so we offer community members neighborhood prizes and reward money in recognition of their contributions. We believe that by working together, we can build a strong community and make valuable open source tools and resources available to more people. So if you are interested in openim-sdk-core, please join our community and start contributing your ideas and skills!
+
+We take notes of each [biweekly meeting](https://github.com/openimsdk/Open-IM-Server/issues/381) in [GitHub discussions](https://github.com/openimsdk/Open-IM-Server/discussions/categories/meeting), and our minutes are written in [Google Docs](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing).
+
+openim-sdk-core maintains a [public roadmap](https://github.com/openimsdk/community/tree/main/roadmaps). It gives a a high-level view of the main priorities for the project, the maturity of different features and projects, and how to influence the project direction.
+
+## about OpenIM
+
+### common
+
+- https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
+- https://github.com/openimsdk/community: Community Management for OpenIM
+
+### OpenIM **Links**
+
+Contains some common parts of the OpenIM community.
+
+- https://github.com/openimsdk/automation: OpenIM Automation, cicd, and actions, Robotics.
+- https://github.com/openimsdk/openim-sdk-core: The IMSDK implemented by golang can be used in iOS, Android, PC and other platforms.
+- https://github.com/openimsdk/openim-sdk-core: Instant messaging IM server.
+- https://github.com/openimsdk/community: Community Management for OpenIM.
+
+### SDKs
+
+- [openim-sdk-core](https://github.com/openimsdk/openim-sdk-core): A cross-platform SDK implemented in golang that can be used in iOS, Android, PC, and other platforms.
+- [Open-IM-SDK-iOS](https://github.com/openimsdk/Open-IM-SDK-iOS): An iOS SDK generated based on openim-sdk-core, available for developers to reference.
+- [Open-IM-SDK-Android](https://github.com/openimsdk/Open-IM-SDK-Android): An Android SDK generated based on openim-sdk-core, available for developers to reference.
+- [Open-IM-SDK-Flutter](https://github.com/openimsdk/Open-IM-SDK-Flutter): A Flutter SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
+- [Open-IM-SDK-Uniapp](https://github.com/openimsdk/Open-IM-SDK-Uniapp): A uni-app SDK generated based on Open-IM-SDK-iOS and Open-IM-SDK-Android, available for developers to reference.
+
+### Demos
+
+- [Open-IM-iOS-Demo](https://github.com/openimsdk/Open-IM-iOS-Demo): An iOS demo based on Open-IM-SDK-iOS, available for developers to reference.
+- [Open-IM-Android-Demo](https://github.com/openimsdk/Open-IM-Android-Demo): An Android demo based on Open-IM-SDK-Android, available for developers to reference.
+- [Open-IM-Flutter-Demo](https://github.com/openimsdk/Open-IM-Flutter-Demo): A Flutter demo based on Open-IM-SDK-Flutter, available for developers to reference.
+
+## Used By
+
+OpenIM is used by the following companies ,let's write it down in [ADOPTER](https://github.com/openimsdk/community/blob/main/ADOPTERS.md).
+
+Please leave your use cases in the comments [here](https://github.com/openimsdk/Open-IM-Server/issues/379).
+
+## License
+
+For more details, please refer to [here](./LICENSE).
+
+## Thanks to our contributors!
+
+
+
+
diff --git a/cmd/gordon_main.go b/cmd/gordon_main.go
deleted file mode 100644
index 3db79a00f..000000000
--- a/cmd/gordon_main.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "context"
- "encoding/json"
- "errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/test"
- "github.com/openimsdk/tools/log"
- "time"
-)
-
-var (
- //APIADDR = "http://43.155.69.205:10002"
- //WSADDR = "ws://43.155.69.205:10001"
- //APIADDR = "https://chat-api-dev.opencord.so"
- //WSADDR = "wss://chat-ws-dev.opencord.so"
- APIADDR = "http://14.29.168.56:10002"
- WSADDR = "ws://14.29.168.56:10001"
- //APIADDR = "http://113.108.8.93:10002"
- //WSADDR = "ws://113.108.8.93:10001"
- REGISTERADDR = APIADDR + "/user_register"
- ACCOUNTCHECK = APIADDR + "/manager/account_check"
- TOKENADDR = APIADDR + "/auth/user_token"
- SECRET = "openIM123"
- //SECRET = "4zbF9Y6Fs1QJ0hsmpC3B676txZcCnjcZ"
- SENDINTERVAL = 20
-)
-var ctx context.Context
-
-const PlatformID = 3
-
-type ResToken struct {
- Data struct {
- ExpiredTime int64 `json:"expiredTime"`
- Token string `json:"token"`
- Uid string `json:"uid"`
- }
- ErrCode int `json:"errCode"`
- ErrMsg string `json:"errMsg"`
-}
-
-func ggetToken(uid string) string {
- url := TOKENADDR
- var req server_api_params.UserTokenReq
- req.Platform = PlatformID
- req.UserID = uid
- req.Secret = SECRET
- req.OperationID = utils.OperationIDGenerator()
- r, err := network.Post2Api(url, req, "a")
- if err != nil {
- log.ZError(ctx, "Post2Api failed ", errors.New("Post2Api failed "), "operationID", req.OperationID, "url", url, "req", req)
- return ""
- }
- var stcResp ResToken
- err = json.Unmarshal(r, &stcResp)
- if stcResp.ErrCode != 0 {
- log.ZError(ctx, "ErrCode failed ", errors.New("ErrCode failed "), "operationID", req.OperationID,
- "errorCode", stcResp.ErrCode, "errMsg", stcResp.ErrMsg, "url", url, "req", req)
- return ""
- }
- log.ZInfo(ctx, "get token: ", "operationID", req.OperationID, "token", stcResp.Data.Token)
- return stcResp.Data.Token
-}
-
-func gRunGetToken(strMyUid string) string {
- var token string
- for true {
- token = ggetToken(strMyUid)
- if token == "" {
- time.Sleep(time.Duration(100) * time.Millisecond)
- continue
- } else {
- break
- }
- }
- return token
-}
-func main() {
- uid := "1695766238"
- //Gordon
- //uid:="1554321956297519104"
- //Gordon2
- //uid := "1583984945064968192"
- //uid := "3734595565"
- tokenx := gRunGetToken(uid)
- //tokenx := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI3MDcwMDgxNTMiLCJQbGF0Zm9ybSI6IkFuZHJvaWQiLCJleHAiOjE5NjY0MTJ1XjJZGWj5fB3mqC7p6ytxSarvxZfsABwIjoxNjUxMDU1MDU2fQ.aWvmJ_sQxXmT5nKwiM5QsF9-tfkldzOYZtRD3nrUuko"
- test.InOutDoTest(uid, tokenx, WSADDR, APIADDR)
- time.Sleep(time.Second * 30)
- // test.DoTestSendMsg2("7789", "7788")
- //test.DoTestGetAdvancedHistoryMessageList()
- //test.DoTestGetSelfUserInfo()
- //test.DoTestSendMsg2GroupWithMessage(uid, "1623878302774460418", "2")
- //test.DoTestAddMessageReactionExtensions(1,"special handle")
- //time.Sleep(time.Second*5)
- //test.DoTestAddMessageReactionExtensions(2,"special handle")
- //time.Sleep(time.Second*5)
- //test.DoTestGetMessageListReactionExtensions("special handle")
- //test.DoTestSetAppBadge()
- //test.DoTestSearchLocalMessages()
- //test.DoTestGetAdvancedHistoryMessageList()
- println("start")
- //test.DoTestGetUserInDepartment()
- //test.DoTestGetDepartmentMemberAndSubDepartment()
- //test.DoTestDeleteAllMsgFromLocalAndSvr()
- // test.DoTestGetDepartmentMemberAndSubDepartment()
- //test.DotestUploadFile()
- //test.DotestMinio()
- //test.DotestSearchFriends()
- //if *senderNum == 0 {
- // test.RegisterAccounts(*onlineNum)
- // return
- //}
- //
- //test.OnlineTest(*onlineNum)
- ////test.TestSendCostTime()
- //test.ReliabilityTest(*singleSenderMsgNum, *intervalTime, 10, *senderNum)
- //test.DoTestSearchLocalMessages()
- //println("start")
- //test.DoTestSendImageMsg(strMyUidx, "17726378428")
- //test.DoTestSearchGroups()
- //test.DoTestGetHistoryMessage("")
- //test.DoTestGetHistoryMessageReverse("")
- //test.DoTestInviteInGroup()
- //test.DoTestCancel()
- //test.DoTestSendMsg2(strMyUidx, friendID)
- //test.DoTestGetAllConversation()
-
- //test.DoTestGetOneConversation("17726378428")
- //test.DoTestGetConversations(`["single_17726378428"]`)
- //test.DoTestGetConversationListSplit()
- //test.DoTestGetConversationRecvMessageOpt(`["single_17726378428"]`)
-
- //set batch
- //test.DoTestSetConversationRecvMessageOpt([]string{"single_17726378428"}, constant.NotReceiveMessage)
- //set one
- ////set batch
- //test.DoTestSetConversationRecvMessageOpt([]string{"single_17726378428"}, constant.ReceiveMessage)
- ////set one
- //test.DoTestSetConversationPinned("single_17726378428", false)
- //test.DoTestSetOneConversationRecvMessageOpt("single_17726378428", constant.NotReceiveMessage)
- //test.DoTestSetOneConversationPrivateChat("single_17726378428", false)
- //test.DoTestReject()
- //test.DoTestAccept()
- //test.DoTestMarkGroupMessageAsRead()
- //test.DoTestGetGroupHistoryMessage()
- //test.DoTestGetHistoryMessage("17396220460")
- time.Sleep(250000 * time.Millisecond)
- //b := utils.GetCurrentTimestampBySecond()
- i := 0
- for {
- //test.DoTestSendMsg2Group(strMyUidx, "42c9f515cb84ee0e82b3f3ce71eb14d6", i)
- i++
- time.Sleep(250 * time.Millisecond)
- //if i == 100 {
- // break
- //}
- //log.Warn("", "10 * time.Millisecond ###################waiting... msg: ", i)
- }
- //
- //log.Warn("", "cost time: ", utils.GetCurrentTimestampBySecond()-b)
- //return
- //i = 0
- //for {
- // //test.DoTestSendMsg2Group(strMyUidx, "42c9f515cb84ee0e82b3f3ce71eb14d6", i)
- // i++
- // time.Sleep(1000 * time.Millisecond)
- // if i == 10 {
- // break
- // }
- // log.Warn("", "1000 * time.Millisecond ###################waiting... msg: ", i)
- //}
- //
- //i = 0
- //for {
- // test.DoTestSendMsg2Group(strMyUidx, "42c9f515cb84ee0e82b3f3ce71eb14d6", i)
- // i++
- // time.Sleep(10000 * time.Millisecond)
- // log.Warn("", "10000 * time.Millisecond ###################waiting... msg: ", i)
- //}
-
- //reliabilityTest()
- // test.PressTest(testClientNum, intervalSleep, imIP)
-}
-
-//
-//funcation main() {
-// testClientNum := 100
-// intervalSleep := 2
-// imIP := "43.128.5.63"
-
-//
-// msgNum := 1000
-// test.ReliabilityTest(msgNum, intervalSleep, imIP)
-// for i := 0; i < 6; i++ {
-// test.Msgwg.Wait()
-// }
-//
-// for {
-//
-// if test.CheckReliabilityResult() {
-// log.Warn("CheckReliabilityResult ok, again")
-//
-// } else {
-// log.Warn("CheckReliabilityResult failed , wait.... ")
-// }
-//
-// time.Sleep(time.Duration(10) * time.Second)
-// }
-//
-//}
-
-//funcation printCallerNameAndLine() string {
-// pc, _, line, _ := runtime.Caller(2)
-// return runtime.FuncForPC(pc).Name() + "()@" + strconv.Itoa(line) + ": "
-//}
-
-// myuid, maxuid, msgnum
diff --git a/cmd/main.go b/cmd/main.go
deleted file mode 100644
index 7e555866e..000000000
--- a/cmd/main.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/test"
- "time"
-)
-
-func main() {
- APIADDR := "http://59.36.173.89:10002"
- WSADDR := "ws://59.36.173.89:10001"
- REGISTERADDR := APIADDR + "/user_register"
- ACCOUNTCHECK := APIADDR + "/manager/account_check"
- TOKENADDR := APIADDR + "/auth/user_token"
- SECRET := "openIM123"
- SENDINTERVAL := 20
- test.REGISTERADDR = REGISTERADDR
- test.TOKENADDR = TOKENADDR
- test.SECRET = SECRET
- test.SENDINTERVAL = SENDINTERVAL
- test.WSADDR = WSADDR
- test.ACCOUNTCHECK = ACCOUNTCHECK
- strMyUidx := "5284951719"
-
- tokenx := test.RunGetToken(strMyUidx)
- fmt.Println(tokenx)
- test.InOutDoTest(strMyUidx, tokenx, WSADDR, APIADDR)
- time.Sleep(time.Second * 10)
- // test.DoTestGetUsersInfo()
- // test.DoTestSetMsgDestructTime("sg_1012596513")
- // test.DoTestRevoke()
- // test.DotestDeleteFriend("8303492153")
- // test.TestMarkGroupMessageAsRead()
- // test.DoTestRevoke()
- // time.Sleep(time.Second * 5)
- // test.DoTestAddToBlackList("9169012630")
- // test.DoTestDeleteFromBlackList("9169012630")
- // test.DotestDeleteFriend("9169012630")
- // test.DoTestSetConversationPinned("si_2456093263_9169012630", true)
- // test.DoTestSetOneConversationRecvMessageOpt("si_2456093263_9169012630", 2)
- // test.DoTestGetConversationRecvMessageOpt("si_2456093263_9169012630")
- // test.DoTestDeleteConversationMsgFromLocalAndSvr("sg_537415520")
- for {
- time.Sleep(10000 * time.Millisecond)
- }
-
-}
diff --git a/cmd/online_open_im.go b/cmd/online_open_im.go
deleted file mode 100644
index 80a0eba71..000000000
--- a/cmd/online_open_im.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-func main() {
- // var onlineNum *int //Number of online users
- // onlineNum = flag.Int("on", 10, "online num")
- // flag.Parse()
- // log.Warn("", "online test start, online num: ", *onlineNum)
- // test.OnlineTest(*onlineNum)
- // log.Warn("", "online test finish, online num: ", *onlineNum)
- // select {}
-}
diff --git a/cmd/parse.go b/cmd/parse.go
deleted file mode 100644
index 34a2a07e1..000000000
--- a/cmd/parse.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "go/types"
-)
-
-func main() {
-
- //filePath, err := filepath.Abs(".\test.go")
- //if err != nil {
- // panic(err)
- //}
- // 解析Go文件
- fset := token.NewFileSet()
- node, err := parser.ParseFile(fset, "D:\\Goland\\workspace\\Open-IM-SDK-Core\\main\\test.go", nil, parser.AllErrors)
- if err != nil {
- panic(err)
- }
- //myImporter := importer.Default()
- // 创建类型检查器
- //conf := types.Config{Importer: myImporter}
- info := &types.Info{
- Defs: make(map[*ast.Ident]types.Object),
- }
- //// 类型检查
- //_, err = conf.Check("", fset, []*ast.File{node}, info)
- //if err != nil {
- // panic(err)
- //}
- // 遍历文件中所有函数
-
- fn := func(pkg *types.Package) string {
- return pkg.Name()
- }
- for _, decl := range node.Decls {
- if f, ok := decl.(*ast.FuncDecl); ok {
- // 打印函数名
- fmt.Println("Function Name: ", f.Name.Name)
- // 打印参数名和类型
- for _, param := range f.Type.Params.List {
- for _, name := range param.Names {
- obj := info.ObjectOf(name)
- typ := obj.Type()
- fmt.Printf("Parameter Name: %s, Type: %s\n", name.Name, types.TypeString(typ, fn))
- }
- }
- }
- }
-}
diff --git a/cmd/press_open_im.go b/cmd/press_open_im.go
deleted file mode 100644
index bf52fc3a1..000000000
--- a/cmd/press_open_im.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "errors"
- "flag"
- "github.com/openimsdk/openim-sdk-core/v3/test"
- "github.com/openimsdk/tools/log"
-)
-
-func main() {
- var senderNum *int //Number of users sending messages
- var singleSenderMsgNum *int //Number of single user send messages
- var intervalTime *int //Sending time interval, in millisecond
- senderNum = flag.Int("sn", 100, "sender num")
- singleSenderMsgNum = flag.Int("mn", 1000, "single sender msg num")
- intervalTime = flag.Int("t", 0, "interval time mill second")
- flag.Parse()
- // test.InitMgr(*senderNum)
-
- log.ZInfo(ctx, "logLevel", uint32(test.LogLevel))
- log.ZWarn(ctx, "press test begin ", errors.New(""), "sender num", *senderNum, " single sender msg num", *singleSenderMsgNum, " send msg total num ", *senderNum**singleSenderMsgNum)
- test.PressTest(*singleSenderMsgNum, *intervalTime, *senderNum)
- select {}
-}
diff --git a/cmd/reliability_open_im.go b/cmd/reliability_open_im.go
deleted file mode 100644
index 07a15d6fb..000000000
--- a/cmd/reliability_open_im.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "errors"
- "flag"
- "github.com/openimsdk/openim-sdk-core/v3/test"
- "github.com/openimsdk/tools/log"
-)
-
-func main() {
- var senderNum *int //Number of users sending messages
- var singleSenderMsgNum *int //Number of single user send messages
- var intervalTime *int //Sending time interval, in millisecond
-
- senderNum = flag.Int("sn", 200, "sender num")
- singleSenderMsgNum = flag.Int("mn", 100, "single sender msg num")
- intervalTime = flag.Int("t", 10, "interval time mill second")
- flag.Parse()
- test.InitMgr(*senderNum)
- log.ZInfo(ctx, "logName", test.LogName, "logLevel", uint32(test.LogLevel))
- log.ZWarn(ctx, "reliability test start ", errors.New(""), "sender num", *senderNum, " single sender msg num", *singleSenderMsgNum, " send msg total num ", *senderNum**singleSenderMsgNum)
-
- test.ReliabilityTest(*singleSenderMsgNum, *intervalTime, 10, *senderNum)
-}
diff --git a/cmd/sk_main.go b/cmd/sk_main.go
deleted file mode 100644
index b797984ac..000000000
--- a/cmd/sk_main.go
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/test"
- "github.com/openimsdk/tools/log"
- "time"
-)
-
-var allDB []*db.DataBase
-
-//funcation TestDB(loginUserID string) {
-// operationID := utils.OperationIDGenerator()
-// dbUser, err := db.NewDataBase(loginUserID, "/data/test/Open-IM-Server/db/sdk/", operationID)
-// if err != nil {
-// log.Error(operationID, "NewDataBase failed ", err.Error(), loginUserID)
-// return
-// }
-// conversationList, err := dbUser.GetAllConversationList()
-// if err != nil {
-// log.Error(operationID, "GetAllConversationList failed ", err.Error())
-// }
-// log.Info(operationID, "GetAllConversationList len: ", len(conversationList))
-//
-// groupIDList, err := dbUser.GetJoinedGroupList()
-// if err != nil {
-// log.Error(operationID, "GetJoinedGroupList failed ", err.Error())
-// }
-// log.Info(operationID, "GetJoinedGroupList len: ", len(groupIDList))
-//
-// groupMemberList, err := dbUser.GetAllGroupMemberList()
-// if err != nil {
-// log.Error(operationID, "GetAllGroupMemberList failed ", err.Error())
-// }
-// log.Info(operationID, "GetAllGroupMemberList len: ", len(groupMemberList))
-// //GetAllMessageForTest
-// msgList, err := dbUser.GetAllMessageForTest()
-// if err != nil {
-// log.Error(operationID, "GetAllMessageForTest failed ", err.Error())
-// }
-// log.Info(operationID, "GetAllMessageForTest len: ", len(msgList))
-// allDB = append(allDB, dbUser)
-//
-// dbUser.CloseDB(operationID)
-// log.Info(operationID, "close db finished ")
-//
-//}
-
-func main() {
- //var userIDList []string
- //f, err := os.Open("/data/test/Open-IM-Server/db/sdk")
- //if err != nil {
- // log.Error("", "open failed ", err.Error())
- // return
- //}
- //files, err := f.Readdir(-1)
- //f.Close()
- //if err != nil {
- // log.Error("", "Readdir failed ", err.Error())
- // return
- //}
- //
- //for _, file := range files {
- // begin := strings.Index(file.Name(), "OpenIM_v2_")
- // end := strings.Index(file.Name(), ".db")
- // userID := file.Name()[begin+len("OpenIM_v2_") : end]
- // // OpenIM_v2_3380999461.db
- // log.Info("", "file name: ", file.Name(), userID)
- // TestDB(userID)
- //}
- //log.Info("", "files: ", len(allDB))
- ////for _, v := range allDB {
- //// v.CloseDB("aa")
- ////}
- //
- //log.Info("", "gc begin ")
- //runtime.GC()
- //log.Info("", "gc end ")
- //time.Sleep(100000 * time.Second)
- //return
- strMyUidx := "3370431052"
- tokenx := test.RunGetToken(strMyUidx)
- //tokenx := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI3MDcwMDgxNTMiLCJQbGF0Zm9ybSI6IkFuZHJvaWQiLCJleHAiOjE5NjY0MTJ1XjJZGWj5fB3mqC7p6ytxSarvxZfsABwIjoxNjUxMDU1MDU2fQ.aWvmJ_sQxXmT5nKwiM5QsF9-tfkldzOYZtRD3nrUuko"
- //go funcation() {
- // time.Sleep(2 * time.Second)
- // test.InOutLogou()
- //}()
-
- test.InOutDoTest(strMyUidx, tokenx, test.WSADDR, test.APIADDR)
- // test.InOutDoTest(strMyUidx, tokenx, test.WSADDR, test.APIADDR)
-
- // time.Sleep(5 * time.Second)
- // test.SetListenerAndLogin(strMyUidx, tokenx)
- //test.DoTestSetGroupMemberInfo("1104164664", "3188816039", "set ex")
-
- // test.DotestGetGroupMemberList()
- //time.Sleep(100000 * time.Second)
- // test.DoTestCreateGroup()
-
- // test.DoTestJoinGroup()
- // test.DoTestGetGroupsInfo()
- // test.DoTestDeleteAllMsgFromLocalAndSvr()
-
- // println("token ", tokenx)
- time.Sleep(100000 * time.Second)
- b := utils.GetCurrentTimestampBySecond()
- i := 0
- for {
- test.DoTestSendMsg2c2c(strMyUidx, "3380999461", i)
- i++
- time.Sleep(100 * time.Millisecond)
- if i == 10000 {
- break
- }
- log.ZWarn(ctx, "", errors.New(""), "10 * time.Millisecond ###################waiting... msg: ", i)
- }
-
- //log.Warn("", "cost time: ", utils.GetCurrentTimestampBySecond()-b)
- time.Sleep(100000 * time.Second)
- return
- i = 0
- for {
- test.DoTestSendMsg2Group(strMyUidx, "42c9f515cb84ee0e82b3f3ce71eb14d6", i)
- i++
- time.Sleep(1000 * time.Millisecond)
- if i == 10 {
- break
- }
- log.ZWarn(ctx, "", errors.New(""), "1000 * time.Millisecond ###################waiting... msg: ", i)
- }
-
- i = 0
- for {
- test.DoTestSendMsg2Group(strMyUidx, "42c9f515cb84ee0e82b3f3ce71eb14d6", i)
- i++
- time.Sleep(10000 * time.Millisecond)
- log.ZWarn(ctx, "", errors.New(""), "10000 * time.Millisecond ###################waiting... msg: ", i)
- }
-
- //reliabilityTest()
- // test.PressTest(testClientNum, intervalSleep, imIP)
-}
-
-//
-//funcation main() {
-// testClientNum := 100
-// intervalSleep := 2
-// imIP := "43.128.5.63"
-
-//
-// msgNum := 1000
-// test.ReliabilityTest(msgNum, intervalSleep, imIP)
-// for i := 0; i < 6; i++ {
-// test.Msgwg.Wait()
-// }
-//
-// for {
-//
-// if test.CheckReliabilityResult() {
-// log.Warn("CheckReliabilityResult ok, again")
-//
-// } else {
-// log.Warn("CheckReliabilityResult failed , wait.... ")
-// }
-//
-// time.Sleep(time.Duration(10) * time.Second)
-// }
-//
-//}
-
-//funcation printCallerNameAndLine() string {
-// pc, _, line, _ := runtime.Caller(2)
-// return runtime.FuncForPC(pc).Name() + "()@" + strconv.Itoa(line) + ": "
-//}
-
-// myuid, maxuid, msgnum
diff --git a/CHANGELOG/CHANGELOG.md b/docs/CHANGELOG.md
similarity index 100%
rename from CHANGELOG/CHANGELOG.md
rename to docs/CHANGELOG.md
diff --git a/docs/gomobile-android-ios-setup-cn.md b/docs/gomobile-android-ios-setup-cn.md
new file mode 100644
index 000000000..ce414c302
--- /dev/null
+++ b/docs/gomobile-android-ios-setup-cn.md
@@ -0,0 +1,154 @@
+# 使用 gomobile 编译 openim-sdk-core 以供 Android/iOS 使用
+
+
+
+ English •
+ 中文
+
+
+
+## 环境准备
+### 1.Go语言环境以及gomobile环境搭建
+
+**1. 安装Go语言环境**
+
+- 安装[GO语言(1.18以上版本)](https://go.dev/dl/)。
+
+**2. 配置GOPATH环境变量**
+
+- 通过 go env 查看 Go 的环境变量设置。以下是典型的输出示例:
+
+ ```bash
+ > go env
+ set GOPATH=D:\Go\gopath
+ set GOMODCACHE=D:\Go\gopath\pkg\mod
+ ```
+- 根据 go env 的输出,将 GOPATH 添加到系统的环境变量中:
+ - **Windows**: 右键点击“此电脑”,选择“属性 -> 高级系统设置 -> 环境变量”,将 GOPATH 的路径(如 D:\Go\gopath)添加到 Path 变量中。
+ - **Mac/Linux**: 在终端中编辑 ~/.bashrc 或 ~/.zshrc,添加以下内容:
+ ```bash
+ export GOPATH=/Users/youruser/go # 根据你的实际路径替换
+ export PATH=$PATH:$GOPATH/bin
+ ```
+
+**3. 安装 gomobile 和 gobind**
+
+在 Go 1.18 或更高版本中,执行以下命令安装最新的 gomobile 和 gobind:
+```bash
+go install golang.org/x/mobile/cmd/gomobile@latest
+go install golang.org/x/mobile/cmd/gobind@latest
+```
+**4. 初始化 gomobile**
+
+执行以下命令完成 gomobile 的初始化:
+```bash
+gomobile init
+```
+### 在 Windows 平台编译 Android AAR 包
+
+#### 环境要求
+
+1. **确保 Go 和 gomobile 配置正确**:执行 `gomobile version` 验证工具是否安装成功。
+
+2. **安装 Android 开发环境**:确保已安装最新版本的 **Android Studio**。
+
+3. **配置 Android NDK**:下载适用于 Windows 的 NDK(推荐版本:`r20b`),将其解压到 Android SDK 的 `ndk-bundle` 目录下。例如:
+
+ ```
+ C:\Users\Admin\AppData\Local\Android\Sdk\ndk-bundle
+ ```
+
+4. **配置 Make 命令支持(可选)**:
+
+ - Windows 不自带 `make`,可以安装 MinGW64:
+
+ 1. 下载 MinGW64 并安装。
+ 2. 安装后,将 MinGW 的 `bin` 目录(如 `C:\mingw64\bin`)添加到系统的环境变量中。
+- 如果无法安装 `make`,你可以直接使用 `gomobile bind` 命令完成编译。
+
+#### 编译AAR包
+
+进入你的项目根目录,例如 `openim-sdk-core`,运行以下命令以编译 Android AAR 包:
+```bash
+gomobile bind -v -trimpath -ldflags="-s -w" -o ./open_im_sdk.aar -target=android ./open_im_sdk/ ./open_im_sdk_callback/
+```
+
+##### **注意事项**:
+
+1. 确保网络畅通,编译过程中需要从 GitHub 下载依赖包。首次运行可能需要较长时间。
+2. 如果使用 MinGW64,可以执行:
+ ```bash
+ mingw32-make android
+ ```
+3. 编译完成后,你会看到类似以下的输出:
+ ```bash
+ aar: jni/armeabi-v7a/libgojni.so
+ aar: jni/arm64-v8a/libgojni.so
+ aar: jni/x86/libgojni.so
+ aar: jni/x86_64/libgojni.so
+ aar: R.txt
+ aar: res/
+ ...
+ ```
+
+ `open_im_sdk.aar` 文件会生成在当前目录。
+
+4. 将生成的 AAR 包通过 Android Studio 的本地导入方式引入到项目中,即可使用导出的函数和回调接口。
+
+
+
+### 在 macOS 平台编译 Android AAR 包和 iOS xcframework
+
+#### 环境要求
+
+1. 安装 Xcode:确保已安装 Xcode(建议版本:15.4 或更高)。
+2. 安装 Android Studio:确保已安装并配置好 Android SDK 和 NDK(Mac 推荐 NDK 版本为:20.0.5594570)。
+
+#### 编译Android AAR包
+
+进入项目根目录(如 `openim-sdk-core`),运行以下命令:
+
+
+```bash
+make android
+```
+编译完成后,将生成的 AAR 包导入到 Android Studio 项目中。
+
+#### 编译iOS xcframework库
+
+1. 进入项目根目录。
+
+2. 执行以下命令以编译 iOS 的 xcframework:
+ ```bash
+ make ios
+ ```
+
+3. 编译完成后,将生成的 `.xcframework` 文件导入到 Xcode 项目中。
+
+
+
+### 常见问题及解决方案
+
+1. **卡在写入 `go.mod` 文件**
+
+- 可能是网络原因导致依赖下载失败。尝试设置 Go 的代理:
+ ```bash
+ go env -w GOPROXY=https://proxy.golang.org,direct
+ ```
+ 如果在国内,可以使用:
+ ```bash
+ go env -w GOPROXY=https://goproxy.cn,direct
+ ```
+
+2. **找不到 `make` 命令**
+
+- 在 Windows 上,确保安装 MinGW64 并使用 `mingw32-make` 命令代替 `make`。
+- 或直接运行 `gomobile bind` 命令。
+
+3. **NDK 版本兼容问题**
+
+- 如果遇到 NDK 版本兼容问题,尝试切换到推荐的版本(`r20b` 或 `20.0.5594570`),并确保路径正确。
+
+
+
+通过 `gomobile`,可以轻松将 Go 语言编写的openim-sdk-core代码打包为 Android 的 AAR 包或 iOS 的 xcframework 库,方便在移动平台中集成和使用。
diff --git a/go.mod b/go.mod
index 507fc327c..6879626c8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,39 +1,65 @@
module github.com/openimsdk/openim-sdk-core/v3
-go 1.21
+go 1.22.7
+
+toolchain go1.22.10
require (
github.com/golang/protobuf v1.5.4
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.4.0
- github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/pkg/errors v0.9.1
- google.golang.org/protobuf v1.33.0 // indirect
+ google.golang.org/protobuf v1.35.1
gorm.io/driver/sqlite v1.5.5
nhooyr.io/websocket v1.8.10
)
-require golang.org/x/net v0.22.0
+require golang.org/x/net v0.29.0 // indirect
require (
github.com/google/go-cmp v0.6.0
- github.com/openimsdk/protocol v0.0.69-alpha.30
- github.com/openimsdk/tools v0.0.49-alpha.44
+ github.com/openimsdk/protocol v0.0.72-alpha.70
+ github.com/openimsdk/tools v0.0.50-alpha.21
github.com/patrickmn/go-cache v2.1.0+incompatible
golang.org/x/image v0.15.0
+ golang.org/x/sync v0.8.0
+ google.golang.org/grpc v1.68.0
gorm.io/gorm v1.25.10
)
require (
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.9.1 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.6 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
- google.golang.org/grpc v1.62.1 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.27.0 // indirect
+ golang.org/x/sys v0.25.0 // indirect
+ golang.org/x/text v0.18.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
+
+replace nhooyr.io/websocket => github.com/coder/websocket v1.8.10
diff --git a/go.sum b/go.sum
index 4bd3df8af..895dd6738 100644
--- a/go.sum
+++ b/go.sum
@@ -1,12 +1,39 @@
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/coder/websocket v1.8.10 h1:K+NrQte1lq04N7V/E3avmuuuCGEaInbjTWukHZsN17g=
+github.com/coder/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
@@ -17,28 +44,56 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
+github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/openimsdk/protocol v0.0.69-alpha.30 h1:OXzCIpDpIY/GI6h1SDYWN51OS9Xv/BcHaOwq8whPKqI=
-github.com/openimsdk/protocol v0.0.69-alpha.30/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8=
-github.com/openimsdk/tools v0.0.49-alpha.44 h1:anefO8hvQwJrYL+V4ifSge/CveZHIpjPeik6BgETuG8=
-github.com/openimsdk/tools v0.0.49-alpha.44/go.mod h1:zc0maZ2ohXlHd0ylY5JnCE8uqq/hslhcfcKa6iO5PCU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/openimsdk/protocol v0.0.72-alpha.70 h1:j7vB81+rTthijRda2b8tlli9oWvPxr4yXHwZ8nPZIBQ=
+github.com/openimsdk/protocol v0.0.72-alpha.70/go.mod h1:Iet+piS/jaS+kWWyj6EEr36mk4ISzIRYjoMSVA4dq2M=
+github.com/openimsdk/tools v0.0.50-alpha.21 h1:ZKgSFkiBjz6KcNZlNwvrSoUYJ7K5Flan8wHuRBH3VqY=
+github.com/openimsdk/tools v0.0.50-alpha.21/go.mod h1:h1cYmfyaVtgFbKmb1Cfsl8XwUOMTt8ubVUQrdGtsUh4=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
@@ -47,25 +102,36 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
+golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
-google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
-google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
+golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
+google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
+google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
+google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
+google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
-nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
-nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/integration_test/README.md b/integration_test/README.md
new file mode 100644
index 000000000..dc80272be
--- /dev/null
+++ b/integration_test/README.md
@@ -0,0 +1,6 @@
+**Package dependency relationship**
+
+```
+process -> manager -> sdk_user_simulator -> vars -> sdkserver
+ sdk -> vars
+```
\ No newline at end of file
diff --git a/integration_test/internal/checker/common.go b/integration_test/internal/checker/common.go
new file mode 100644
index 000000000..064fc3f17
--- /dev/null
+++ b/integration_test/internal/checker/common.go
@@ -0,0 +1,27 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/tools/log"
+)
+
+const (
+ errChanLen = 10
+)
+
+var (
+ checkErrChan = make(chan error, errChanLen)
+)
+
+func InsertToErrChan(ctx context.Context, err error) {
+ select {
+ case checkErrChan <- err:
+ default:
+ log.ZDebug(ctx, "checkErrChan is full")
+ }
+}
+
+func CloseAndGetCheckErrChan() <-chan error {
+ close(checkErrChan)
+ return checkErrChan
+}
diff --git a/integration_test/internal/checker/conversation.go b/integration_test/internal/checker/conversation.go
new file mode 100644
index 000000000..6389eed45
--- /dev/null
+++ b/integration_test/internal/checker/conversation.go
@@ -0,0 +1,52 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+)
+
+// CheckConvNumAfterImpFriAndCrGro check conversation num after import friends and create groups.
+func CheckConvNumAfterImpFriAndCrGro(ctx context.Context) error {
+ corrects := func() [2]int {
+ // corrects[0] :super user conversion num
+ // corrects[1] :common user conversion num
+ largeNum := vars.LargeGroupNum
+ commonNum := 0 // cal by userNum
+ groupNum := largeNum + commonNum
+
+ superNum := vars.UserNum - 1 + groupNum
+ commonUserNum := vars.SuperUserNum + groupNum
+
+ return [2]int{superNum, commonUserNum}
+ }()
+
+ c := &CounterChecker[*sdk.TestSDK, string]{
+ CheckName: "checkConversationNum",
+ CheckerKeyName: "userID",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) {
+ totalNum, err := t.GetTotalConversationCount(ctx)
+ if err != nil {
+ return 0, err
+ }
+ return totalNum, nil
+ },
+ CalCorrectCount: func(userID string) int {
+ commonGroupNum := calCommonGroup(utils.MustGetUserNum(userID))
+ if utils.IsSuperUser(userID) {
+ return corrects[0] + commonGroupNum
+ } else {
+ return corrects[1] + commonGroupNum
+ }
+ },
+ LoopSlice: sdk.TestSDKs,
+ GetKey: func(t *sdk.TestSDK) string {
+ return t.UserID
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
diff --git a/integration_test/internal/checker/counter.go b/integration_test/internal/checker/counter.go
new file mode 100644
index 000000000..34747776e
--- /dev/null
+++ b/integration_test/internal/checker/counter.go
@@ -0,0 +1,163 @@
+package checker
+
+import (
+ "context"
+ "fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/stringutil"
+ "strings"
+ "sync"
+ "time"
+)
+
+type Counter struct {
+ TotalCount int
+ CorrectCount int
+ IsEqual bool
+}
+
+func NewCounter(total, correct int, isEqual bool) *Counter {
+ return &Counter{
+ TotalCount: total,
+ CorrectCount: correct,
+ IsEqual: isEqual,
+ }
+}
+
+type CounterChecker[T any, K comparable] struct {
+ // CheckName must start with 'check' and be named with a small camel hump,
+ // followed by the name of the indicator that needs to be checked,
+ // and it will be assigned to checkNumName.
+ // e.g. checkGroupNum: checkNumName=GroupNum
+ CheckName string
+ checkNumName string // used for printing logs
+ CheckerKeyName string // used for printing logs
+ GoroutineLimit int
+ GetTotalCount func(ctx context.Context, t T) (int, error) // get now total count
+ CalCorrectCount func(key K) int // return correct num
+ LoopSlice []T // circular slicing
+ GetKey func(t T) K // get checkers key from a type
+}
+
+func (c *CounterChecker[T, K]) Init() {
+ c.CheckName = stringutil.LowerFirst(c.CheckName)
+ c.checkNumName = strings.TrimPrefix(c.CheckName, "check")
+}
+
+func (c *CounterChecker[T, K]) Check(ctx context.Context) error {
+ defer decorator.FuncLogSkip(ctx, 1)()
+
+ c.Init()
+
+ var (
+ gr, cctx = reerrgroup.WithContext(ctx, c.GoroutineLimit)
+ total int
+ now int
+
+ checkers = make(map[K]*Counter, len(sdk.TestSDKs))
+ mapLock = sync.RWMutex{}
+ )
+ total = len(c.LoopSlice)
+ progress.FuncBarPrint(cctx, stringutil.GetFuncName(1), gr, now, total)
+
+ for _, t := range c.LoopSlice {
+ t := t
+ gr.Go(func() error {
+ key := c.GetKey(t)
+ correctNum := c.CalCorrectCount(key)
+ totalNum, err := c.GetTotalCount(ctx, t)
+ if err != nil {
+ return err
+ }
+ isEqual := totalNum == correctNum
+ if !isEqual {
+ mapLock.Lock()
+ checkers[key] = NewCounter(totalNum, correctNum, isEqual)
+ mapLock.Unlock()
+ }
+ return nil
+ })
+ }
+ if err := gr.Wait(); err != nil {
+ return err
+ }
+
+ if len(checkers) != 0 {
+ err := errs.New(fmt.Sprintf("%s un correct!", stringutil.CamelCaseToSpaceSeparated(c.CheckName))).Wrap()
+ for k, ck := range checkers {
+ log.ZWarn(ctx, fmt.Sprintf("%s un correct", stringutil.CamelCaseToSpaceSeparated(c.checkNumName)),
+ err, c.CheckerKeyName, k, c.checkNumName, ck.TotalCount, "correct num", ck.CorrectCount)
+ }
+ InsertToErrChan(ctx, err)
+ } else {
+ log.ZInfo(ctx, fmt.Sprintf("%s success", stringutil.CamelCaseToSpaceSeparated(c.CheckName)))
+ }
+ return nil
+}
+
+func (c *CounterChecker[T, K]) LoopCheck(ctx context.Context) error {
+ defer decorator.FuncLogSkip(ctx, 1)()
+
+ c.Init()
+
+ var (
+ gr, cctx = reerrgroup.WithContext(ctx, c.GoroutineLimit)
+ total int
+ now int
+ )
+ total = len(c.LoopSlice)
+ p := progress.FuncBarPrint(cctx, stringutil.GetFuncName(1), gr, now, total)
+
+ for _, t := range c.LoopSlice {
+ t := t
+ checkCount := 0
+ isEqual := false
+ gr.Go(func() error {
+ key := c.GetKey(t)
+ correctNum := c.CalCorrectCount(key)
+
+ bar := progress.NewRemoveBar(fmt.Sprintf("%v", key), 0, correctNum)
+ p.AddBar(bar)
+
+ for !isEqual {
+
+ totalNum, err := c.GetTotalCount(ctx, t)
+ if err != nil {
+ return err
+ }
+
+ p.SetBarNow(bar, totalNum)
+
+ isEqual = totalNum == correctNum
+ if !isEqual {
+ checkCount++
+
+ checkMsg := fmt.Sprintf("check num:%d, %s un correct", checkCount, stringutil.CamelCaseToSpaceSeparated(c.checkNumName))
+
+ log.ZWarn(ctx, checkMsg, nil, c.CheckerKeyName, key, c.checkNumName, totalNum, "correct num", correctNum)
+
+ if checkCount == config.MaxCheckLoopNum {
+ return errs.New(checkMsg, c.CheckerKeyName, key, c.checkNumName, totalNum, "correct num", correctNum).Wrap()
+ }
+ } else {
+ log.ZInfo(ctx, fmt.Sprintf("check num:%d, %s correct",
+ checkCount, stringutil.CamelCaseToSpaceSeparated(c.checkNumName)),
+ c.CheckerKeyName, key, c.checkNumName, totalNum, "correct num", correctNum)
+ }
+ time.Sleep(config.CheckWaitSec * time.Second)
+ }
+ return nil
+ })
+ }
+ if err := gr.Wait(); err != nil {
+ return err
+ }
+ log.ZInfo(ctx, fmt.Sprintf("%s success", stringutil.CamelCaseToSpaceSeparated(c.CheckName)))
+ return nil
+}
diff --git a/integration_test/internal/checker/group.go b/integration_test/internal/checker/group.go
new file mode 100644
index 000000000..ae01ee69a
--- /dev/null
+++ b/integration_test/internal/checker/group.go
@@ -0,0 +1,53 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+)
+
+func CheckGroupNum(ctx context.Context) error {
+ correct := func() int {
+ largeNum := vars.LargeGroupNum
+ commonNum := 0 // cal by userNum
+ return largeNum + commonNum
+ }()
+
+ c := &CounterChecker[*sdk.TestSDK, string]{
+ CheckName: "checkGroupNum",
+ CheckerKeyName: "userID",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) {
+ _, groupNum, err := t.GetAllJoinedGroups(ctx)
+ if err != nil {
+ return 0, err
+ }
+ return groupNum, nil
+ },
+ CalCorrectCount: func(userID string) int {
+ return correct + calCommonGroup(utils.MustGetUserNum(userID))
+ },
+ LoopSlice: sdk.TestSDKs,
+ GetKey: func(t *sdk.TestSDK) string {
+ return t.UserID
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
+
+func calCommonGroup(userNum int) int {
+
+ preNum := utils.NextOffsetNum(userNum, -(vars.CommonGroupMemberNum - 1))
+
+ createNum := 0
+ for i := 0; i < vars.CommonGroupMemberNum; i++ {
+ if utils.IsNumLogin(preNum) {
+ createNum++
+ }
+ preNum = utils.NextNum(preNum)
+ }
+ return createNum * vars.CommonGroupNum
+}
diff --git a/integration_test/internal/checker/msg.go b/integration_test/internal/checker/msg.go
new file mode 100644
index 000000000..314f9d516
--- /dev/null
+++ b/integration_test/internal/checker/msg.go
@@ -0,0 +1,118 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+)
+
+// CheckMessageNum check message num.
+func CheckMessageNum(ctx context.Context) error {
+ createdLargeGroupNum := vars.LargeGroupNum / vars.LoginUserNum
+ corrects := func() [3]int {
+ // corrects[0]: super user msg num
+ // corrects[1]: common user msg num
+ // corrects[2]: create more one large group largest user no + 1
+
+ // if a user num smaller than remainder, it means this user created more one large group
+ remainder := vars.LargeGroupNum % vars.LoginUserNum
+
+ largeGroupNum :=
+ // total send message num +
+ vars.GroupMessageNum*vars.LoginUserNum*vars.LargeGroupNum +
+ // total create group notification message -
+ vars.LargeGroupNum
+ // self send group message(cal by userID) -
+ // self create group notification message. Complete the calculation based on user ID in CalCorrectCount.
+
+ commonGroupNum := 0
+ // self create group notification message
+ // Formula:
+ // commonGroupNum =
+ // total send group message(cal by userID) +
+ // total create group notification message(cal by userID) -
+ // self send group message(cal by userID) -
+ // self create group notification message(cal by userID)
+
+ groupMsgNum := largeGroupNum + commonGroupNum
+
+ superUserMsgNum := 0
+ // Formula:
+ // superUserMsgNum =
+ // friend send message num(cal by userID) +
+ // become friend notification message num(cal by userID)
+
+ commonUserMsgNum := min(vars.LoginUserNum, vars.SuperUserNum) * vars.SingleMessageNum
+
+ return [3]int{superUserMsgNum + groupMsgNum, commonUserMsgNum + groupMsgNum, remainder}
+ }()
+
+ c := &CounterChecker[*sdk.TestSDK, string]{
+ CheckName: "checkMessageNum",
+ CheckerKeyName: "userID",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) {
+ totalNum, err := t.SDK.Conversation().GetTotalUnreadMsgCount(ctx)
+ if err != nil {
+ return 0, err
+ }
+ return int(totalNum), nil
+ },
+ CalCorrectCount: func(userID string) int {
+ var res int
+ userNum := utils.MustGetUserNum(userID)
+ if utils.IsSuperUser(userID) {
+ res += corrects[0]
+ res += vars.UserNum - 1 - userNum // become friend notification message num
+ if userNum < vars.LoginUserNum {
+ // friend send message num
+ res += vars.SingleMessageNum * (vars.LoginUserNum - 1)
+ // self send large group message
+ res -= vars.GroupMessageNum * vars.LargeGroupNum
+ res -= createdLargeGroupNum
+ } else {
+ // friend send message num
+ res += vars.SingleMessageNum * vars.LoginUserNum
+ // self send large group message
+ res -= 0
+ }
+ } else {
+ res += corrects[1]
+ if userNum < vars.LoginUserNum {
+ // self send large group message
+ res -= vars.GroupMessageNum * vars.LargeGroupNum
+ // self created large group num
+ res -= createdLargeGroupNum
+ } else {
+ // self send large group message
+ res -= 0
+ }
+ }
+
+ // commonGroupNum =
+ // total send group message(cal by userID) +
+ // total create group notification message(cal by userID) -
+ // self send group message(cal by userID) -
+ // self create group notification message(cal by userID)
+ comGroupNum := calCommonGroup(userNum) * (vars.GroupMessageNum + 1)
+ if utils.IsNumLogin(userNum) {
+ comGroupNum -= vars.CommonGroupNum * (vars.GroupMessageNum + 1)
+ }
+ res += comGroupNum
+
+ // create more one large group
+ if userNum < corrects[2] {
+ res--
+ }
+ return res
+ },
+ LoopSlice: sdk.TestSDKs,
+ GetKey: func(t *sdk.TestSDK) string {
+ return t.UserID
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
diff --git a/integration_test/internal/checker/relation.go b/integration_test/internal/checker/relation.go
new file mode 100644
index 000000000..36dff11bb
--- /dev/null
+++ b/integration_test/internal/checker/relation.go
@@ -0,0 +1,45 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+)
+
+// CheckLoginUsersFriends check login users friends
+func CheckLoginUsersFriends(ctx context.Context) error {
+ corrects := func() [2]int {
+ // corrects[0] :super user friend num
+ // corrects[1] :common user friend num
+
+ return [2]int{vars.UserNum - 1, vars.SuperUserNum}
+ }()
+
+ c := &CounterChecker[*sdk.TestSDK, string]{
+ CheckName: "checkLoginUsersFriends",
+ CheckerKeyName: "userID",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t *sdk.TestSDK) (int, error) {
+ friendList, err := t.GetAllFriends(ctx)
+ if err != nil {
+ return 0, err
+ }
+ return len(friendList), nil
+ },
+ CalCorrectCount: func(userID string) int {
+ if utils.IsSuperUser(userID) {
+ return corrects[0]
+ } else {
+ return corrects[1]
+ }
+ },
+ LoopSlice: sdk.TestSDKs[:vars.LoginUserNum],
+ GetKey: func(t *sdk.TestSDK) string {
+ return t.UserID
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
diff --git a/integration_test/internal/checker/user.go b/integration_test/internal/checker/user.go
new file mode 100644
index 000000000..ec4f6c895
--- /dev/null
+++ b/integration_test/internal/checker/user.go
@@ -0,0 +1,56 @@
+package checker
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+)
+
+func CheckLoginByRateNum(ctx context.Context) error {
+ correct := func() int {
+ return vars.LoginUserNum
+ }()
+
+ c := &CounterChecker[int, string]{
+ CheckName: "checkLoginByRateNum",
+ CheckerKeyName: "loginNum",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t int) (int, error) {
+ return int(vars.NowLoginNum.Load()), nil
+ },
+ CalCorrectCount: func(_ string) int {
+ return correct
+ },
+ LoopSlice: []int{0},
+ GetKey: func(t int) string {
+ return "login"
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
+
+// CheckAllLoginNum check if all user is login
+func CheckAllLoginNum(ctx context.Context) error {
+ correct := func() int {
+ return vars.UserNum
+ }()
+
+ c := &CounterChecker[int, string]{
+ CheckName: "checkLoginByRateNum",
+ CheckerKeyName: "loginNum",
+ GoroutineLimit: config.ErrGroupCommonLimit,
+ GetTotalCount: func(ctx context.Context, t int) (int, error) {
+ return int(vars.NowLoginNum.Load()), nil
+ },
+ CalCorrectCount: func(_ string) int {
+ return correct
+ },
+ LoopSlice: []int{0},
+ GetKey: func(t int) string {
+ return "login"
+ },
+ }
+
+ return c.LoopCheck(ctx)
+}
diff --git a/integration_test/internal/config/config.go b/integration_test/internal/config/config.go
new file mode 100644
index 000000000..a9a518554
--- /dev/null
+++ b/integration_test/internal/config/config.go
@@ -0,0 +1,32 @@
+package config
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/constant"
+)
+
+const (
+ TestIP = "127.0.0.1"
+ APIAddr = "http://" + TestIP + ":10002"
+ WsAddr = "ws://" + TestIP + ":10001"
+ AdminUserID = "imAdmin"
+ Secret = "openIM123"
+ PlatformID = constant.AndroidPlatformID
+ LogLevel = 3
+ DataDir = "./data/"
+ LogFilePath = "./logs/"
+ IsLogStandardOutput = false
+)
+
+func GetConf() sdk_struct.IMConfig {
+ var cf sdk_struct.IMConfig
+ cf.ApiAddr = APIAddr
+ cf.WsAddr = WsAddr
+ cf.DataDir = DataDir
+ cf.LogLevel = LogLevel
+ cf.IsExternalExtensions = true
+ cf.PlatformID = int32(PlatformID)
+ cf.LogFilePath = LogFilePath
+ cf.IsLogStandardOutput = IsLogStandardOutput
+ return cf
+}
diff --git a/integration_test/internal/config/limit.go b/integration_test/internal/config/limit.go
new file mode 100644
index 000000000..f335f7df6
--- /dev/null
+++ b/integration_test/internal/config/limit.go
@@ -0,0 +1,27 @@
+package config
+
+const (
+ MaxUserNum = 1e+5 // max users
+)
+
+const (
+ ErrGroupSmallLimit = 20 // max goroutine of a small error group
+ ErrGroupMiddleSmallLimit = 50 // max goroutine of a middle small error group
+ ErrGroupCommonLimit = 100 // max goroutine of a common error group
+)
+
+const (
+ SleepSec = 30
+ CheckWaitSec = 5 // check wait sec
+ BarRemoveWaiteSec = 1 // progress bar remove wait second
+)
+
+const (
+ CheckMsgRate = 1 // Sampling and statistical message ratio. Max check message is MaxCheckMsg
+ MaxCheckMsg = 1e+8
+ MaxCheckLoopNum = 40
+)
+
+const (
+ ApiParamLength = 1000
+)
diff --git a/integration_test/internal/config/statistics.go b/integration_test/internal/config/statistics.go
new file mode 100644
index 000000000..322b81b87
--- /dev/null
+++ b/integration_test/internal/config/statistics.go
@@ -0,0 +1,7 @@
+package config
+
+const (
+ ReceiveMsgTimeThresholdLow = 1 // Receive msg time threshold low. Unit: s
+ ReceiveMsgTimeThresholdMedium = 3 // Receive msg time threshold medium. Unit: s
+ ReceiveMsgTimeThresholdHigh = 5 // Receive msg time threshold high. Unit: s
+)
diff --git a/integration_test/internal/manager/file_manager.go b/integration_test/internal/manager/file_manager.go
new file mode 100644
index 000000000..8248ad6c6
--- /dev/null
+++ b/integration_test/internal/manager/file_manager.go
@@ -0,0 +1,32 @@
+package manager
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/tools/errs"
+ "os"
+)
+
+type TestFileManager struct {
+ *MetaManager
+}
+
+func NewFileManager(m *MetaManager) *TestFileManager {
+ return &TestFileManager{m}
+}
+
+func (m *TestFileManager) DeleteLocalDB(ctx context.Context) error {
+ defer decorator.FuncLog(ctx)()
+
+ conf := config.GetConf()
+ err := os.RemoveAll(conf.DataDir)
+ if err != nil {
+ return errs.WrapMsg(err, "remove db failed")
+ }
+ err = os.MkdirAll(conf.DataDir, os.ModePerm)
+ if err != nil {
+ return errs.WrapMsg(err, "make db dir failed")
+ }
+ return nil
+}
diff --git a/integration_test/internal/manager/group_manager.go b/integration_test/internal/manager/group_manager.go
new file mode 100644
index 000000000..431328b98
--- /dev/null
+++ b/integration_test/internal/manager/group_manager.go
@@ -0,0 +1,103 @@
+package manager
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ sdkUtils "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/stringutil"
+)
+
+type TestGroupManager struct {
+ *MetaManager
+}
+
+func NewGroupManager(m *MetaManager) *TestGroupManager {
+ return &TestGroupManager{m}
+}
+
+// CreateGroups creates group chats. It needs to create both large group chats and regular group chats.
+// The number of large group chats to be created is specified by vars.LargeGroupNum, and the group owner cycles from 0 to vars.UserNum.
+// Every user creates regular group chats, and the number of regular group chats to be created is specified by vars.CommonGroupNum.
+func (m *TestGroupManager) CreateGroups(ctx context.Context) error {
+ defer decorator.FuncLog(ctx)()
+
+ gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupCommonLimit)
+ var (
+ total int
+ now int
+ )
+ total = vars.LargeGroupNum + vars.LoginUserNum
+ p := progress.FuncNameBarPrint(cctx, gr, now, total)
+
+ m.createLargeGroups(ctx, gr, p)
+ m.createCommonGroups(ctx, gr, p)
+ return gr.Wait()
+}
+
+// createLargeGroups see CreateGroups
+func (m *TestGroupManager) createLargeGroups(ctx context.Context, gr *reerrgroup.Group, p *progress.Progress) {
+ userNum := 0
+
+ bar := progress.NewRemoveBar(stringutil.GetSelfFuncName(), 0, vars.LargeGroupNum)
+ p.AddBar(bar)
+
+ for i := 0; i < vars.LargeGroupNum; i++ {
+
+ ctx := vars.Contexts[userNum]
+ testSDK := sdk.TestSDKs[userNum]
+ gr.Go(func() error {
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ log.ZWarn(ctx, "createLargeGroups begin", nil)
+ _, err := testSDK.CreateLargeGroup(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZWarn(ctx, "createLargeGroups end", nil)
+
+ p.IncBar(bar)
+
+ return nil
+ })
+ userNum = utils.NextLoginNum(userNum)
+ }
+ return
+}
+
+// createLargeGroups see CreateGroups
+func (m *TestGroupManager) createCommonGroups(ctx context.Context, gr *reerrgroup.Group, p *progress.Progress) {
+
+ bar := progress.NewRemoveBar(stringutil.GetSelfFuncName(), 0, vars.CommonGroupNum*vars.LoginUserNum)
+ p.AddBar(bar)
+
+ for userNum := 0; userNum < vars.LoginUserNum; userNum++ {
+ userNum := userNum
+ ctx := vars.Contexts[userNum]
+ testSDK := sdk.TestSDKs[userNum]
+
+ gr.Go(func() error {
+ ubar := progress.NewRemoveBar(utils.GetUserID(userNum), 0, vars.CommonGroupNum)
+ p.AddBar(ubar)
+ for i := 0; i < vars.CommonGroupNum; i++ {
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ log.ZWarn(ctx, "createCommonGroups begin", nil)
+ _, err := testSDK.CreateCommonGroup(ctx, vars.CommonGroupMemberNum)
+ if err != nil {
+ return err
+ }
+ log.ZWarn(ctx, "createCommonGroups end", nil)
+
+ p.IncBar(bar, ubar)
+ }
+ return nil
+ })
+ }
+ return
+}
diff --git a/integration_test/internal/manager/manager.go b/integration_test/internal/manager/manager.go
new file mode 100644
index 000000000..361322d4c
--- /dev/null
+++ b/integration_test/internal/manager/manager.go
@@ -0,0 +1,86 @@
+package manager
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ authPB "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/tools/mcontext"
+)
+
+type MetaManager struct {
+ sdk_struct.IMConfig
+ secret string
+ token string
+}
+
+func NewMetaManager() *MetaManager {
+ conf := config.GetConf()
+ return &MetaManager{
+ IMConfig: conf,
+ secret: config.Secret,
+ }
+}
+
+func (m *MetaManager) ApiPost(ctx context.Context, route string, req, resp any) (err error) {
+ return network.ApiPost(ctx, route, req, resp)
+}
+
+// PostWithCtx should only be used for scenarios such as registration and login that do not require a token or
+// require an admin token.
+// For scenarios that require a specific user token, please obtain the context from vars.Contexts for the request.
+func (m *MetaManager) PostWithCtx(route string, req, resp any) error {
+ return m.ApiPost(m.BuildCtx(nil), route, req, resp)
+}
+
+// BuildCtx build an admin token
+func (m *MetaManager) BuildCtx(ctx context.Context) context.Context {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ ctx = ccontext.WithInfo(ctx, &ccontext.GlobalConfig{
+ Token: m.token,
+ IMConfig: m.IMConfig,
+ })
+ ctx = ccontext.WithOperationID(ctx, utils.OperationIDGenerator())
+ ctx = mcontext.SetOpUserID(ctx, "admin")
+ return ctx
+}
+
+func (m *MetaManager) GetSecret() string {
+ return m.secret
+}
+
+func (m *MetaManager) GetAdminToken(userID string, platformID int32) (string, error) {
+ req := authPB.GetAdminTokenReq{UserID: userID, Secret: m.secret}
+ resp := authPB.GetAdminTokenResp{}
+ err := m.PostWithCtx(api.GetAdminToken.Route(), &req, &resp)
+ if err != nil {
+ return "", err
+ }
+ return resp.Token, nil
+}
+
+func (m *MetaManager) GetUserToken(userID string, platformID int32) (string, error) {
+ req := authPB.GetUserTokenReq{PlatformID: platformID, UserID: userID}
+ resp := authPB.GetUserTokenResp{}
+ err := m.PostWithCtx(api.GetUsersToken.Route(), &req, &resp)
+ if err != nil {
+ return "", err
+ }
+ return resp.Token, nil
+}
+
+func (m *MetaManager) WithAdminToken() (err error) {
+ token, err := m.GetAdminToken(config.AdminUserID, config.PlatformID)
+ if err != nil {
+ return err
+ }
+ m.token = token
+ return nil
+}
diff --git a/integration_test/internal/manager/msg_manager.go b/integration_test/internal/manager/msg_manager.go
new file mode 100644
index 000000000..f3441651c
--- /dev/null
+++ b/integration_test/internal/manager/msg_manager.go
@@ -0,0 +1,148 @@
+package manager
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ sdkUtils "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+type TestMsgManager struct {
+ *MetaManager
+}
+
+func NewMsgManager(m *MetaManager) *TestMsgManager {
+ return &TestMsgManager{m}
+}
+
+// SendMessages send messages.The rules are: each user sends `vars.SingleMessageNum` messages to all friends,
+// and sends `vars.GroupMessageNum` messages to all large groups and common groups created by themselves.
+func (m *TestMsgManager) SendMessages(ctx context.Context) error {
+ defer decorator.FuncLog(ctx)()
+
+ gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupCommonLimit)
+
+ var (
+ total int
+ now int
+ )
+ total = vars.LoginUserNum * 2
+ p := progress.FuncNameBarPrint(cctx, gr, now, total)
+
+ m.sendSingleMessages(ctx, gr, p)
+ m.sendGroupMessages(ctx, gr, p)
+ return gr.Wait()
+}
+
+// sendSingleMessages see SendMessages
+func (m *TestMsgManager) sendSingleMessages(ctx context.Context, gr *reerrgroup.Group, p *progress.Progress) {
+ for userNum := 0; userNum < vars.LoginUserNum; userNum++ {
+ userNum := userNum
+ ctx := vars.Contexts[userNum]
+ testSDK := sdk.TestSDKs[userNum]
+ gr.Go(func() error {
+
+ friends, err := testSDK.GetAllFriends(ctx)
+ if err != nil {
+ return err
+ }
+
+ bar := progress.NewRemoveBar(fmt.Sprintf("%s:%s", "sendSingleMessages", utils.GetUserID(userNum)),
+ 0, len(friends)*vars.SingleMessageNum)
+ p.AddBar(bar)
+
+ friends = datautil.ShuffleSlice(friends)
+ for _, friend := range friends {
+ if friend != nil {
+ for i := 0; i < vars.SingleMessageNum; i++ {
+ msg, err := testSDK.SDK.Conversation().CreateTextMessage(ctx,
+ fmt.Sprintf("count %d:my userID is %s", i, testSDK.UserID))
+ if err != nil {
+ return err
+ }
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ t := time.Now()
+ log.ZWarn(ctx, "sendSingleMessages begin", nil)
+ _, err = testSDK.SendSingleMsg(ctx, msg, friend.FriendUserID)
+ if err != nil {
+ return err
+ }
+ log.ZWarn(ctx, "sendSingleMessages end", nil, "time cost:", time.Since(t))
+ p.IncBar(bar)
+
+ time.Sleep(time.Millisecond * 500)
+
+ }
+ } else {
+ fmt.Println("what`s this???")
+ }
+ }
+ log.ZWarn(ctx, "send over", nil, "userID", userNum)
+ return nil
+ })
+ }
+ return
+}
+
+// sendGroupMessages see SendMessages
+func (m *TestMsgManager) sendGroupMessages(ctx context.Context, gr *reerrgroup.Group, p *progress.Progress) {
+ for userNum := 0; userNum < vars.LoginUserNum; userNum++ {
+ userNum := userNum
+ ctx := vars.Contexts[userNum]
+ testSDK := sdk.TestSDKs[userNum]
+ gr.Go(func() error {
+ groups, _, err := testSDK.GetAllJoinedGroups(ctx)
+ if err != nil {
+ return err
+ }
+ sendGroups := make([]string, 0)
+ for _, group := range groups {
+ if int(group.MemberCount) == vars.LargeGroupMemberNum || group.OwnerUserID == testSDK.UserID {
+ // is larger group or created by oneself
+ sendGroups = append(sendGroups, group.GroupID)
+ }
+ }
+
+ bar := progress.NewRemoveBar(fmt.Sprintf("%s:%s", "sendGroupMessages", utils.GetUserID(userNum)),
+ 0, len(sendGroups)*vars.GroupMessageNum)
+ p.AddBar(bar)
+
+ sendGroups = datautil.ShuffleSlice(sendGroups)
+ for _, group := range sendGroups {
+ group := group
+ for i := 0; i < vars.GroupMessageNum; i++ {
+ msg, err := testSDK.SDK.Conversation().CreateTextMessage(ctx,
+ fmt.Sprintf("count %d:my userID is %s", i, testSDK.UserID))
+ if err != nil {
+ return err
+ }
+
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ t := time.Now()
+ log.ZWarn(ctx, "sendGroupMessages begin", nil)
+ _, err = testSDK.SendGroupMsg(ctx, msg, group)
+ if err != nil {
+ return err
+ }
+ log.ZWarn(ctx, "sendGroupMessages end", nil, "time cost:", time.Since(t))
+
+ p.IncBar(bar)
+ time.Sleep(time.Millisecond * 500)
+ }
+ }
+ return nil
+ })
+ }
+ return
+}
diff --git a/integration_test/internal/manager/relation_manager.go b/integration_test/internal/manager/relation_manager.go
new file mode 100644
index 000000000..bf0f5bff2
--- /dev/null
+++ b/integration_test/internal/manager/relation_manager.go
@@ -0,0 +1,68 @@
+package manager
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/tools/log"
+)
+
+type TestRelationManager struct {
+ *MetaManager
+}
+
+func NewRelationManager(m *MetaManager) *TestRelationManager {
+ return &TestRelationManager{m}
+}
+
+// ImportFriends Import all users as friends of the superuser (excluding themselves),
+// making the superuser have all users as friends,
+// while regular users have the superuser as their only friend.
+// A superuser is defined as a user who has all users as friends,
+// their IDs range from 0 to vars.SuperUserNum.
+func (m *TestRelationManager) ImportFriends(ctx context.Context) error {
+ defer decorator.FuncLog(ctx)()
+
+ gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupMiddleSmallLimit)
+
+ var (
+ total int
+ now int
+ )
+ total = vars.SuperUserNum
+ progress.FuncNameBarPrint(cctx, gr, now, total)
+ for i, userID := range vars.SuperUserIDs {
+ i := i
+ userID := userID
+ gr.Go(func() error {
+ friendIDs := vars.UserIDs[i+1:] // excluding oneself
+ if len(friendIDs) == 0 {
+ return nil
+ }
+
+ for i := 0; i < len(friendIDs); i += config.ApiParamLength {
+ end := i + config.ApiParamLength
+ if end > len(friendIDs) {
+ end = len(friendIDs)
+ }
+ req := &relation.ImportFriendReq{
+ OwnerUserID: userID,
+ FriendUserIDs: friendIDs[i:end],
+ }
+ ctx := m.BuildCtx(ctx)
+ log.ZWarn(ctx, "ImportFriends begin", nil, "len", len(friendIDs))
+ if err := api.ImportFriendList.Execute(ctx, req); err != nil {
+ return err
+ }
+ log.ZWarn(ctx, "ImportFriends end", nil, "len", len(friendIDs))
+ }
+ return nil
+ })
+ }
+ return gr.Wait()
+}
diff --git a/integration_test/internal/manager/user_manager.go b/integration_test/internal/manager/user_manager.go
new file mode 100644
index 000000000..87ae34bb1
--- /dev/null
+++ b/integration_test/internal/manager/user_manager.go
@@ -0,0 +1,179 @@
+package manager
+
+import (
+ "context"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/progress"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/sdk_user_simulator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ sdkUtils "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/protocol/sdkws"
+ userPB "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+)
+
+type TestUserManager struct {
+ *MetaManager
+}
+
+func NewUserManager(m *MetaManager) *TestUserManager {
+ return &TestUserManager{m}
+}
+
+func (t *TestUserManager) GenAllUserIDs() []string {
+ ids := utils.GenUserIDs(vars.UserNum)
+ vars.UserIDs = ids
+ vars.SuperUserIDs = ids[:vars.SuperUserNum]
+ return ids
+}
+
+func (t *TestUserManager) RegisterAllUsers(ctx context.Context) error {
+ return t.registerUsers(ctx, vars.UserIDs...)
+}
+
+func (t *TestUserManager) registerUsers(ctx context.Context, userIDs ...string) error {
+ defer decorator.FuncLog(ctx)()
+
+ var users []*sdkws.UserInfo
+ for _, userID := range userIDs {
+ users = append(users, &sdkws.UserInfo{UserID: userID, Nickname: userID})
+ }
+
+ for i := 0; i < len(users); i += config.ApiParamLength {
+ end := i + config.ApiParamLength
+ if end > len(users) {
+ end = len(users)
+ }
+ if err := t.PostWithCtx(api.UserRegister.Route(), &userPB.UserRegisterReq{
+ Users: users[i:end],
+ }, nil); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (t *TestUserManager) InitAllSDK(ctx context.Context) error {
+ return t.initSDK(ctx, vars.UserIDs...)
+}
+
+func (t *TestUserManager) initSDK(ctx context.Context, userIDs ...string) error {
+ defer decorator.FuncLog(ctx)()
+
+ gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupCommonLimit)
+
+ var (
+ total int
+ now int
+ )
+ total = len(userIDs)
+ progress.FuncNameBarPrint(cctx, gr, now, total)
+
+ for _, userID := range userIDs {
+ userID := userID
+ gr.Go(func() error {
+ userNum := utils.MustGetUserNum(userID)
+ mgr, err := sdk_user_simulator.InitSDK(userID, t.IMConfig)
+ if err != nil {
+ return err
+ }
+ sdk.TestSDKs[userNum] = sdk.NewTestSDK(userID, userNum, mgr) // init sdk
+ ctx := mgr.Context()
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ ctx = mcontext.SetOpUserID(ctx, userID)
+ ctx, cancel := context.WithCancel(ctx)
+ vars.Contexts[userNum] = ctx // init ctx
+ vars.Cancels[userNum] = cancel
+ log.ZDebug(ctx, "init sdk", "operationID", mcontext.GetOperationID(ctx), "op userID", userID)
+ return nil
+ })
+ }
+ if err := gr.Wait(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (t *TestUserManager) LoginAllUsers(ctx context.Context) error {
+ return t.login(ctx, vars.UserIDs...)
+}
+
+func (t *TestUserManager) LoginByRate(ctx context.Context) error {
+ userIDs := vars.UserIDs[:vars.LoginUserNum]
+ return t.login(ctx, userIDs...)
+}
+
+func (t *TestUserManager) LoginLastUsers(ctx context.Context) error {
+ userIDs := vars.UserIDs[vars.LoginUserNum:]
+ return t.login(ctx, userIDs...)
+}
+
+func (t *TestUserManager) login(ctx context.Context, userIDs ...string) error {
+ defer decorator.FuncLog(ctx)()
+
+ log.ZDebug(ctx, "login users", "len", len(userIDs), "userIDs", userIDs)
+
+ gr, cctx := reerrgroup.WithContext(ctx, config.ErrGroupCommonLimit)
+
+ var (
+ total int
+ now int
+ )
+ total = len(userIDs)
+ progress.FuncNameBarPrint(cctx, gr, now, total)
+ for _, userID := range userIDs {
+ userID := userID
+ gr.Go(func() error {
+ token, err := t.GetUserToken(userID, config.PlatformID)
+ if err != nil {
+ return err
+ }
+ userNum := utils.MustGetUserNum(userID)
+ err = sdk.TestSDKs[userNum].SDK.Login(vars.Contexts[userNum], userID, token)
+ if err != nil {
+ return err
+ }
+ return nil
+ })
+ }
+ if err := gr.Wait(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (t *TestUserManager) ForceLogoutAllUsers(ctx context.Context) error {
+ return t.forceLogout(ctx, vars.UserIDs...)
+}
+
+func (t *TestUserManager) forceLogout(ctx context.Context, userIDs ...string) error {
+ defer decorator.FuncLog(ctx)()
+
+ log.ZDebug(ctx, "logout users", "len", len(userIDs), "userIDs", userIDs)
+
+ cancelBar := progress.NewBar("cancel ctx", 0, len(userIDs), false)
+ pro := progress.Start(cancelBar)
+ for _, userID := range userIDs {
+ pro.IncBar(cancelBar)
+ vars.Cancels[utils.MustGetUserNum(userID)]()
+ }
+
+ sleepTime := 30 * 2 // unit: second. pone wait * 2
+ sleepBar := progress.NewBar("sleep", 0, sleepTime, false)
+ pro.AddBar(sleepBar)
+ for i := 0; i < sleepTime; i++ {
+ time.Sleep(time.Second)
+ pro.IncBar(sleepBar)
+ }
+ return nil
+}
diff --git a/integration_test/internal/pkg/decorator/log.go b/integration_test/internal/pkg/decorator/log.go
new file mode 100644
index 000000000..850b9a78a
--- /dev/null
+++ b/integration_test/internal/pkg/decorator/log.go
@@ -0,0 +1,44 @@
+package decorator
+
+import (
+ "context"
+ "fmt"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/stringutil"
+ "time"
+)
+
+// FuncLog is a log print decorator.
+// Correct usage is: defer decorator.FuncLog(ctx)()
+func FuncLog(ctx context.Context) func() {
+ return FuncLogSkip(ctx, 1)
+}
+
+// FuncLogSkip is a log print decorator. The argument skip is the number of stack frames
+// to ascend.
+// e.g.
+//
+// func FuncName(ctx context.Context){
+// middleFunc(ctx)
+// }
+//
+// func middleFunc(ctx context.Context){
+// FuncLogSkip(ctx, 1)
+// // ...
+// }
+//
+// the funcName is `FuncName`
+func FuncLogSkip(ctx context.Context, skip int) func() {
+ funcName := stringutil.GetFuncName(skip + 1) // +1 is FuncLogSkip
+ return ProgressLog(ctx, funcName)
+}
+
+func ProgressLog(ctx context.Context, name string) func() {
+ t := time.Now()
+ log.ZInfo(ctx, fmt.Sprintf("%s begin", name))
+ fmt.Println(fmt.Sprintf("%s begin", name))
+ return func() {
+ log.ZInfo(ctx, fmt.Sprintf("%s end", name), "time consuming", time.Since(t))
+ fmt.Println(fmt.Sprintf("%s end. Time consuming: %v", name, time.Since(t)))
+ }
+}
diff --git a/integration_test/internal/pkg/decorator/log_test.go b/integration_test/internal/pkg/decorator/log_test.go
new file mode 100644
index 000000000..85e5bee8b
--- /dev/null
+++ b/integration_test/internal/pkg/decorator/log_test.go
@@ -0,0 +1,21 @@
+package decorator
+
+import (
+ "context"
+ "fmt"
+ "testing"
+)
+
+func TestFuncLog(t *testing.T) {
+ FuncName(context.Background())
+}
+
+func FuncName(ctx context.Context) {
+ middleFunc(ctx)
+}
+
+func middleFunc(ctx context.Context) {
+ defer FuncLogSkip(ctx, 1)()
+ //...
+ fmt.Println("middle func")
+}
diff --git a/integration_test/internal/pkg/initialization/flag.go b/integration_test/internal/pkg/initialization/flag.go
new file mode 100644
index 000000000..41c593ac6
--- /dev/null
+++ b/integration_test/internal/pkg/initialization/flag.go
@@ -0,0 +1,89 @@
+package initialization
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/flagconst"
+ "github.com/openimsdk/tools/log"
+)
+
+func InitFlag() {
+ flag.BoolVar(&flagconst.TestMode, vars.FlagMap["TestMode"], false, "mark is test mode")
+
+ flag.IntVar(&vars.UserNum, vars.FlagMap["UserNum"], 100, "user num")
+ flag.IntVar(&vars.SuperUserNum, vars.FlagMap["SuperUserNum"], 10, "number of users with all friends")
+ flag.IntVar(&vars.LargeGroupNum, vars.FlagMap["LargeGroupNum"], 5, "number of big group")
+ flag.IntVar(&vars.LargeGroupMemberNum, vars.FlagMap["LargeGroupMemberNum"], 100, "number of big group member")
+ flag.IntVar(&vars.CommonGroupNum, vars.FlagMap["CommonGroupNum"], 10, "number of small group")
+ flag.IntVar(&vars.CommonGroupMemberNum, vars.FlagMap["CommonGroupMemberNum"], 20, "number of small group member")
+ flag.IntVar(&vars.SingleMessageNum, vars.FlagMap["SingleMessageNum"], 5, "number of single message each user send")
+ flag.IntVar(&vars.GroupMessageNum, vars.FlagMap["GroupMessageNum"], 1, "number of group message each user send")
+
+ flag.BoolVar(&vars.ShouldRegister, vars.FlagMap["ShouldRegister"], false, "determine whether register")
+ flag.BoolVar(&vars.ShouldImportFriends, vars.FlagMap["ShouldImportFriends"], false, "determine whether import friends")
+ flag.BoolVar(&vars.ShouldCreateGroup, vars.FlagMap["ShouldCreateGroup"], false, "determine whether create group")
+ flag.BoolVar(&vars.ShouldSendMsg, vars.FlagMap["ShouldSendMsg"], false, "determine whether send messages")
+
+ flag.BoolVar(&vars.ShouldCheckGroupNum, vars.FlagMap["ShouldCheckGroupNum"], false, "determine whether check group num")
+ flag.BoolVar(&vars.ShouldCheckConversationNum, vars.FlagMap["ShouldCheckConversationNum"], false, "determine whether check conversation num")
+ flag.BoolVar(&vars.ShouldCheckMessageNum, vars.FlagMap["ShouldCheckMessageNum"], false, "determine whether check message num")
+ flag.BoolVar(&vars.ShouldCheckUninsAndReins, vars.FlagMap["ShouldCheckUninsAndReins"], false, "determine whether check again after uninstall and reinstall")
+
+ flag.Float64Var(&vars.LoginRate, vars.FlagMap["LoginRate"], 0, "number of login user rate")
+
+}
+
+// SetFlagLimit prevent parameters from exceeding the limit
+func SetFlagLimit() {
+ vars.UserNum = min(vars.UserNum, config.MaxUserNum)
+ vars.CommonGroupMemberNum = min(vars.CommonGroupMemberNum, vars.UserNum)
+
+ vars.LoginRate = min(vars.LoginRate, 1)
+
+ if !isSet(vars.FlagMap["LargeGroupMemberNum"]) {
+ vars.LargeGroupMemberNum = vars.UserNum
+ }
+
+ if isSet(vars.FlagMap["LoginRate"]) {
+ vars.IsLogin = true
+ }
+}
+
+func PrintFlag() {
+ result := fmt.Sprintf(
+ "TestMode-%s:%t, UserNum-%s:%d, SuperUserNum-%s:%d, LargeGroupNum-%s:%d, LargeGroupMemberNum-%s:%d, CommonGroupNum-%s:%d, CommonGroupMemberNum-%s:%d, SingleMessageNum-%s:%d, GroupMessageNum-%s:%d, ShouldRegister-%s:%t, ShouldImportFriends-%s:%t, ShouldCreateGroup-%s:%t, ShouldSendMsg-%s:%t, ShouldCheckGroupNum-%s:%t, ShouldCheckConversationNum-%s:%t, ShouldCheckMessageNum-%s:%t, ShouldCheckUninsAndReins-%s:%t, LoginRate-%s:%.2f",
+ vars.FlagMap["TestMode"], flagconst.TestMode,
+ vars.FlagMap["UserNum"], vars.UserNum,
+ vars.FlagMap["SuperUserNum"], vars.SuperUserNum,
+ vars.FlagMap["LargeGroupNum"], vars.LargeGroupNum,
+ vars.FlagMap["LargeGroupMemberNum"], vars.LargeGroupMemberNum,
+ vars.FlagMap["CommonGroupNum"], vars.CommonGroupNum,
+ vars.FlagMap["CommonGroupMemberNum"], vars.CommonGroupMemberNum,
+ vars.FlagMap["SingleMessageNum"], vars.SingleMessageNum,
+ vars.FlagMap["GroupMessageNum"], vars.GroupMessageNum,
+ vars.FlagMap["ShouldRegister"], vars.ShouldRegister,
+ vars.FlagMap["ShouldImportFriends"], vars.ShouldImportFriends,
+ vars.FlagMap["ShouldCreateGroup"], vars.ShouldCreateGroup,
+ vars.FlagMap["ShouldSendMsg"], vars.ShouldSendMsg,
+ vars.FlagMap["ShouldCheckGroupNum"], vars.ShouldCheckGroupNum,
+ vars.FlagMap["ShouldCheckConversationNum"], vars.ShouldCheckConversationNum,
+ vars.FlagMap["ShouldCheckMessageNum"], vars.ShouldCheckMessageNum,
+ vars.FlagMap["ShouldCheckUninsAndReins"], vars.ShouldCheckUninsAndReins,
+ vars.FlagMap["LoginRate"], vars.LoginRate,
+ )
+ fmt.Println(result)
+ log.ZWarn(context.TODO(), "flags", nil, "flag params", result)
+}
+
+func isSet(fg string) bool {
+ set := false
+ flag.Visit(func(f *flag.Flag) {
+ if f.Name == fg {
+ set = true
+ }
+ })
+ return set
+}
diff --git a/integration_test/internal/pkg/initialization/global.go b/integration_test/internal/pkg/initialization/global.go
new file mode 100644
index 000000000..e884b6a0b
--- /dev/null
+++ b/integration_test/internal/pkg/initialization/global.go
@@ -0,0 +1,20 @@
+package initialization
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "math"
+ "math/rand"
+ "time"
+)
+
+func InitGlobalData() {
+ sdk.TestSDKs = make([]*sdk.TestSDK, vars.UserNum)
+ vars.Contexts = make([]context.Context, vars.UserNum)
+ vars.Cancels = make([]context.CancelFunc, vars.UserNum)
+ vars.LoginUserNum = int(math.Floor(vars.LoginRate * float64(vars.UserNum)))
+ vars.RecvMsgConsuming = make(chan *vars.StatMsg, config.MaxCheckMsg)
+ rand.New(rand.NewSource(time.Now().UnixNano()))
+}
diff --git a/integration_test/internal/pkg/initialization/log.go b/integration_test/internal/pkg/initialization/log.go
new file mode 100644
index 000000000..895d34c35
--- /dev/null
+++ b/integration_test/internal/pkg/initialization/log.go
@@ -0,0 +1,23 @@
+package initialization
+
+import (
+ "fmt"
+
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/version"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/log"
+)
+
+const (
+ rotateCount uint = 1
+ rotationTime uint = 24
+)
+
+func InitLog(cf sdk_struct.IMConfig) error {
+ 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 {
+ fmt.Println("log init failed ", err.Error())
+ return err
+ }
+ return nil
+}
diff --git a/integration_test/internal/pkg/initialization/sdk.go b/integration_test/internal/pkg/initialization/sdk.go
new file mode 100644
index 000000000..b2492a562
--- /dev/null
+++ b/integration_test/internal/pkg/initialization/sdk.go
@@ -0,0 +1,9 @@
+package initialization
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/manager"
+)
+
+func GenUserIDs() {
+ manager.NewUserManager(manager.NewMetaManager()).GenAllUserIDs()
+}
diff --git a/integration_test/internal/pkg/progress/ansi.go b/integration_test/internal/pkg/progress/ansi.go
new file mode 100644
index 000000000..633479ddb
--- /dev/null
+++ b/integration_test/internal/pkg/progress/ansi.go
@@ -0,0 +1,40 @@
+package progress
+
+import "fmt"
+
+// esc generates multiple ANSI control characters
+func esc(suffix ...string) (ansis string) {
+ for _, s := range suffix {
+ ansis += fmt.Sprintf("%c[%s", 033, s)
+ }
+ return
+}
+
+func clearLine() string {
+ return esc("2K")
+}
+
+func cursorHorizontalAbsolute(n int) string {
+ return esc(fmt.Sprintf("%dG", n))
+}
+
+func moveToLineHead() string {
+ return cursorHorizontalAbsolute(1)
+}
+
+func cursorUp(n int) string {
+ return esc(fmt.Sprintf("%dA", n))
+}
+
+// cursorUpHead up and move to head
+func cursorUpHead(n int) string {
+ return esc(fmt.Sprintf("%dF", n))
+}
+
+func saveCursor() string {
+ return esc("s")
+}
+
+func loadCursor() string {
+ return esc("u")
+}
diff --git a/integration_test/internal/pkg/progress/api.go b/integration_test/internal/pkg/progress/api.go
new file mode 100644
index 000000000..4b02e5ab1
--- /dev/null
+++ b/integration_test/internal/pkg/progress/api.go
@@ -0,0 +1,43 @@
+package progress
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/reerrgroup"
+ "github.com/openimsdk/tools/utils/stringutil"
+)
+
+func FuncNameBarPrint(ctx context.Context, gr *reerrgroup.Group, now, total int) *Progress {
+ return FuncBarPrint(ctx, stringutil.GetFuncName(1), gr, now, total)
+}
+
+func FuncBarPrint(ctx context.Context, name string, gr *reerrgroup.Group, now, total int) *Progress {
+ bar := NewBar(name, now, total, false)
+ p := Start(bar)
+ gr.SetAfterTasks(func() error {
+ p.IncBar(bar)
+ return nil
+ })
+
+ go func() {
+ select {
+ case <-ctx.Done():
+ p.Stop()
+ case <-p.done:
+ return // p is done
+ }
+ }()
+
+ return p
+}
+
+func Start(bar ...*Bar) *Progress {
+ return StartWithMode(AutoClose|ForbiddenWrite, bar...)
+}
+
+func StartWithMode(mode proFlag, bar ...*Bar) *Progress {
+ p := NewProgress(mode, 0)
+ p.Start()
+
+ p.AddBar(bar...)
+ return p
+}
diff --git a/integration_test/internal/pkg/progress/bar.go b/integration_test/internal/pkg/progress/bar.go
new file mode 100644
index 000000000..2748f722c
--- /dev/null
+++ b/integration_test/internal/pkg/progress/bar.go
@@ -0,0 +1,53 @@
+package progress
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "time"
+)
+
+func NewBar(name string, now, total int, ifRemove bool) *Bar {
+ return &Bar{
+ name: name,
+ now: now,
+ total: total,
+ ifRemove: ifRemove,
+ delayRemove: config.BarRemoveWaiteSec * time.Second,
+ }
+}
+
+func NewRemoveBar(name string, now, total int) *Bar {
+ return &Bar{
+ name: name,
+ now: now,
+ total: total,
+ ifRemove: true,
+ delayRemove: config.BarRemoveWaiteSec * time.Second,
+ }
+}
+
+type Bar struct {
+ name string
+ now int
+ total int
+ completeTime time.Time
+ delayRemove time.Duration
+ ifRemove bool
+}
+
+func (b *Bar) shouldRemove() bool {
+ if !b.ifRemove || b.now != b.total {
+ return false
+ }
+ if b.completeTime.IsZero() {
+ // first complete
+ b.completeTime = time.Now()
+ }
+ if time.Since(b.completeTime) >= b.delayRemove {
+ return true
+ }
+ return false
+}
+
+func (b *Bar) isDone() bool {
+ return b.now == b.total
+}
diff --git a/integration_test/internal/pkg/progress/progress.go b/integration_test/internal/pkg/progress/progress.go
new file mode 100644
index 000000000..a5b04964d
--- /dev/null
+++ b/integration_test/internal/pkg/progress/progress.go
@@ -0,0 +1,231 @@
+package progress
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/formatutil"
+ "io"
+ "os"
+ "sync"
+)
+
+// Running used to record whether there is a progress currently running. Only one progress is allowed to run at the same time.
+var Running = make(chan struct{}, 1)
+
+type proFlag uint8
+
+const (
+ AutoClose proFlag = 1 << iota // if set, progress will close when all bars done
+ ForbiddenWrite // if set, progress will if forbidden other goroutine print to Stdout through os.Stdout
+)
+
+type signalType uint8
+
+const (
+ start signalType = iota // do
+ update
+ stop
+)
+
+const (
+ maxBars = 10 // default max print bars
+)
+
+func NewProgress(mode proFlag, m int) *Progress {
+ if m == 0 {
+ m = maxBars
+ }
+ return &Progress{
+ signal: make(chan signalType, 1),
+ buf: bytes.Buffer{},
+ done: make(chan struct{}),
+
+ MaxPrintBar: m,
+ mode: mode,
+ }
+}
+
+type Progress struct {
+ pipeWriter *os.File // Acts as a temporary writer for os.Stdout during the write prohibition period
+ // buf used to store the data that is printed when input is disabled, to be output after the prohibition is lifted
+ buf bytes.Buffer
+ done chan struct{} // Used to record whether copying to the buffer is complete
+
+ Bars []*Bar
+ MaxPrintBar int // The maximum number of bars to print; any excess will be recorded but not displayed.
+ printLine int
+
+ mode proFlag
+ signal chan signalType
+ lock sync.Mutex
+}
+
+func (p *Progress) forbiddenPrint() {
+ r, w, _ := os.Pipe()
+ p.pipeWriter = w
+ os.Stdout = p.pipeWriter
+ p.done = make(chan struct{})
+ p.buf = bytes.Buffer{}
+ go func() {
+ _, _ = io.Copy(&p.buf, r)
+ close(p.done)
+ }()
+}
+
+func (p *Progress) allowPrint() {
+ _ = p.pipeWriter.Close()
+ os.Stdout = vars.OsStdout
+ <-p.done
+ // print buf
+ fmt.Print(p.buf.String())
+}
+
+func (p *Progress) AddBar(bs ...*Bar) {
+ if len(bs) == 0 {
+ return
+ }
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ p.Bars = append(p.Bars, bs...)
+ p.notifyUpdate()
+}
+
+func (p *Progress) notifyUpdate() {
+ select {
+ case p.signal <- update:
+ default:
+ }
+}
+
+func (p *Progress) run() {
+ Running <- struct{}{}
+ for {
+ signal := <-p.signal
+ switch signal {
+ case start:
+ p.start()
+ fallthrough
+ case update:
+ p.render()
+ case stop:
+ p.stop()
+ return
+ default:
+ return
+ }
+ }
+}
+
+func (p *Progress) render() {
+ printStr := ""
+
+ printStr += cursorUpHead(p.printLine)
+ p.printLine = 0
+ donePrintLine := 0
+ for i, bar := range p.Bars {
+ // debug
+ if bar.now > bar.total {
+ log.ZError(context.TODO(), "bar data error", errs.New("bar data error"),
+ "name", bar.name, "now", bar.now, "total", bar.total)
+ }
+
+ if bar.shouldRemove() && len(p.Bars) > p.MaxPrintBar {
+ p.lock.Lock()
+ datautil.DeleteAt(&p.Bars, i)
+ p.lock.Unlock()
+ continue
+ }
+ if bar.isDone() {
+ donePrintLine++
+ }
+ if p.printLine < p.MaxPrintBar {
+ printStr += clearLine()
+ printStr += formatutil.ProgressBar(bar.name, bar.now, bar.total)
+ printStr += nextLine()
+ p.printLine++
+ }
+ }
+ p.print(printStr)
+
+ // auto close
+ if donePrintLine == len(p.Bars) && p.mode&AutoClose == AutoClose {
+ select {
+ case p.signal <- stop:
+ default:
+ // still have signal not been completed
+ }
+ }
+}
+
+func (p *Progress) start() {
+ if p.mode&ForbiddenWrite == ForbiddenWrite {
+ p.forbiddenPrint()
+ }
+}
+
+func (p *Progress) stop() {
+ if p.mode&ForbiddenWrite == ForbiddenWrite {
+ p.allowPrint()
+ }
+ //close(p.signal)
+ <-Running
+}
+
+func (p *Progress) print(s string) {
+ _, _ = fmt.Fprint(vars.OsStdout, s)
+}
+
+func nextLine() string {
+ return "\n"
+}
+
+func (p *Progress) Start() {
+ go p.run()
+ p.signal <- start
+}
+
+func (p *Progress) Stop() {
+ if p.IsStopped() {
+ return
+ }
+ p.signal <- stop
+ <-p.done
+}
+
+func (p *Progress) IsStopped() bool {
+ select {
+ case _, _ = <-p.done:
+ // already done
+ return true
+ default:
+ return false
+ }
+}
+
+func (p *Progress) IncBar(bs ...*Bar) {
+ if len(bs) == 0 {
+ return
+ }
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ for i := range bs {
+ bs[i].now++
+ }
+ p.notifyUpdate()
+}
+
+func (p *Progress) SetBarNow(b *Bar, now int) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ b.now = now
+ p.notifyUpdate()
+}
+
+func (p *Progress) SetMaxPrintBar(n int) {
+ p.MaxPrintBar = n
+}
diff --git a/integration_test/internal/pkg/reerrgroup/errgroup.go b/integration_test/internal/pkg/reerrgroup/errgroup.go
new file mode 100644
index 000000000..723803466
--- /dev/null
+++ b/integration_test/internal/pkg/reerrgroup/errgroup.go
@@ -0,0 +1,156 @@
+// Package reerrgroup is a rewrite of errgroup
+package reerrgroup
+
+import (
+ "context"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ checkTaskDoneMilSec = 100 // Check task completion interval (unit: Milliseconds)
+)
+
+// A Group is a collection of goroutines working on subtasks that are part of
+// the same overall task.
+//
+// A zero Group is valid, has no limit on the number of active goroutines,
+// and does not cancel on error.
+type Group struct {
+ cancel func(error)
+ wg sync.WaitGroup // worker wg
+
+ taskChan chan func() error
+ taskCount atomic.Int64
+
+ errOnce sync.Once
+ err error
+
+ beforeTasks []func() error
+ afterTasks []func() error
+}
+
+func (g *Group) done() {
+
+ g.wg.Done()
+}
+
+// WithContext returns a new Group and an associated Context derived from ctx.
+//
+// The derived Context is canceled the first time a function passed to Go
+// returns a non-nil error or the first time Wait returns, whichever occurs
+// first.
+func WithContext(ctx context.Context, workerCount int) (*Group, context.Context) {
+ g := &Group{}
+ g.initialize(workerCount)
+ ctx, cancel := context.WithCancelCause(ctx)
+ g.cancel = cancel
+ return g, ctx
+}
+
+func (g *Group) initialize(workerCount int) {
+ g.taskChan = make(chan func() error, workerCount) // Initialize the channel with the provided buffer size.
+ // Start multiple goroutines based on the specified workerCount.
+ for i := 0; i < workerCount; i++ {
+ g.wg.Add(1)
+ go func() {
+ defer g.wg.Done()
+ for task := range g.taskChan {
+ doTask := func() error {
+ defer g.taskCount.Add(-1)
+ tasks := append(append(g.beforeTasks, task), g.afterTasks...)
+ for _, t := range tasks { // Execute the function
+ if g.err != nil {
+ return nil
+ }
+ if err := t(); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ if err := doTask(); err != nil {
+ g.errOnce.Do(func() {
+ g.err = err
+ })
+ if g.cancel != nil {
+ g.cancel(g.err)
+ }
+ }
+
+ }
+ }()
+ }
+}
+
+// Wait blocks until all function calls from the Go method have returned, then
+// returns the first non-nil error (if any) from them.
+func (g *Group) Wait() error {
+ close(g.taskChan)
+
+ g.wg.Wait()
+
+ if g.cancel != nil {
+ g.cancel(g.err)
+ }
+ return g.err
+}
+
+// WaitTaskDone only wait all task done without cancel ctx and close taskChan.
+func (g *Group) WaitTaskDone() {
+ ticker := time.NewTicker(checkTaskDoneMilSec * time.Millisecond)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ticker.C:
+ if g.taskCount.Load() == 0 {
+ return
+ }
+ }
+ }
+}
+
+// Go calls the given function in a new goroutine.
+// It blocks until the new goroutine can be added without the number of
+// active goroutines in the group exceeding the configured limit.
+//
+// The first call to return a non-nil error cancels the group's context, if the
+// group was created by calling WithContext. The error will be returned by Wait.
+func (g *Group) Go(f func() error) {
+ if g.err != nil {
+ return
+ }
+
+ select {
+ case g.taskChan <- f:
+ g.taskCount.Add(1)
+ return
+ }
+}
+
+// TryGo calls the given function in a new goroutine only if the number of
+// active goroutines in the group is currently below the configured limit.
+//
+// The return value reports whether the goroutine was started.
+func (g *Group) TryGo(f func() error) bool {
+ if g.err != nil {
+ return false
+ }
+
+ select {
+ case g.taskChan <- f:
+ g.taskCount.Add(1)
+ return true
+ default:
+ return false
+ }
+}
+
+func (g *Group) SetBeforeTasks(f ...func() error) {
+ g.beforeTasks = append(g.beforeTasks, f...)
+}
+
+func (g *Group) SetAfterTasks(f ...func() error) {
+ g.afterTasks = append(g.afterTasks, f...)
+}
diff --git a/integration_test/internal/pkg/sdk_user_simulator/listener.go b/integration_test/internal/pkg/sdk_user_simulator/listener.go
new file mode 100644
index 000000000..bf70e987e
--- /dev/null
+++ b/integration_test/internal/pkg/sdk_user_simulator/listener.go
@@ -0,0 +1,279 @@
+package sdk_user_simulator
+
+import (
+ "context"
+ "math/rand"
+
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+)
+
+type conversationCallBack struct {
+}
+
+func (c *conversationCallBack) OnSyncServerFailed(reinstalled bool) {
+}
+
+func (c *conversationCallBack) OnNewConversation(conversationList string) {
+}
+
+func (c *conversationCallBack) OnConversationChanged(conversationList string) {
+}
+
+func (c *conversationCallBack) OnTotalUnreadMessageCountChanged(totalUnreadCount int32) {
+}
+
+func (c *conversationCallBack) OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string) {
+}
+
+func (c *conversationCallBack) OnRecvMessageExtensionsDeleted(msgID string, reactionExtensionKeyList string) {
+}
+
+func (c *conversationCallBack) OnSyncServerProgress(progress int) {
+}
+
+func (c *conversationCallBack) OnSyncServerStart(reinstalled bool) {
+
+}
+
+func (c *conversationCallBack) OnSyncServerFinish(reinstalled bool) {
+
+}
+
+func (c *conversationCallBack) OnConversationUserInputStatusChanged(change string) {
+
+}
+
+type userCallback struct {
+}
+
+func (c userCallback) OnUserStatusChanged(statusMap string) {
+
+}
+
+func (userCallback) OnSelfInfoUpdated(callbackData string) {
+
+}
+func (userCallback) OnUserCommandAdd(callbackData string) {
+
+}
+func (userCallback) OnUserCommandUpdate(callbackData string) {
+
+}
+func (userCallback) OnUserCommandDelete(callbackData string) {
+
+}
+
+type SingleMessage struct {
+ SendID string
+ ClientMsgID string
+ Delay int64
+}
+type MsgListenerCallBak struct {
+ userID string
+ GroupDelay map[string][]*SingleMessage
+ SingleDelay map[string][]*SingleMessage
+}
+
+func NewMsgListenerCallBak(userID string) *MsgListenerCallBak {
+ return &MsgListenerCallBak{userID: userID,
+ GroupDelay: make(map[string][]*SingleMessage),
+ SingleDelay: make(map[string][]*SingleMessage)}
+}
+
+func (m *MsgListenerCallBak) OnRecvNewMessage(message string) {
+ var sm sdk_struct.MsgStruct
+ _ = utils.JsonStringToStruct(message, &sm)
+
+ if rand.Float64() < config.CheckMsgRate && sm.ContentType == constant.Text {
+ rev := utils.GetCurrentTimestampByMill()
+ stm := &vars.StatMsg{
+ CostTime: rev - sm.CreateTime,
+ ReceiveTime: rev,
+ Msg: &sm,
+ }
+ select {
+ case vars.RecvMsgConsuming <- stm:
+ default:
+ }
+ }
+ switch sm.SessionType {
+ case constant.SingleChatType:
+ m.SingleDelay[sm.SendID] =
+ append(m.SingleDelay[sm.SendID], &SingleMessage{SendID: sm.SendID, ClientMsgID: sm.ClientMsgID, Delay: GetRelativeServerTime() - sm.SendTime})
+ case constant.ReadGroupChatType:
+ m.GroupDelay[sm.GroupID] =
+ append(m.GroupDelay[sm.GroupID], &SingleMessage{SendID: sm.SendID, ClientMsgID: sm.ClientMsgID, Delay: GetRelativeServerTime() - sm.SendTime})
+ default:
+ }
+
+}
+
+func (m *MsgListenerCallBak) OnMsgEdited(message string) {
+
+}
+
+func (m *MsgListenerCallBak) OnRecvC2CReadReceipt(msgReceiptList string) {
+}
+
+func (m *MsgListenerCallBak) OnMsgDeleted(s string) {}
+
+func (m *MsgListenerCallBak) OnRecvOfflineNewMessage(message string) {
+}
+
+func (m *MsgListenerCallBak) OnRecvMessageExtensionsAdded(msgID string, reactionExtensionList string) {
+
+}
+
+func (m *MsgListenerCallBak) OnRecvGroupReadReceipt(groupMsgReceiptList string) {
+}
+func (m *MsgListenerCallBak) OnNewRecvMessageRevoked(messageRevoked string) {
+}
+
+func (m *MsgListenerCallBak) OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string) {
+
+}
+func (m *MsgListenerCallBak) OnRecvMessageExtensionsDeleted(msgID string, reactionExtensionKeyList string) {
+}
+
+func (m *MsgListenerCallBak) OnRecvOnlineOnlyMessage(message string) {
+
+}
+
+type testFriendshipListener struct {
+}
+
+func (testFriendshipListener) OnFriendApplicationAdded(callbackInfo string) {
+
+}
+func (testFriendshipListener) OnFriendApplicationDeleted(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnFriendApplicationAccepted(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnFriendApplicationRejected(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnFriendAdded(callbackInfo string) {
+}
+
+func (testFriendshipListener) OnFriendDeleted(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnBlackAdded(callbackInfo string) {
+
+}
+func (testFriendshipListener) OnBlackDeleted(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnFriendInfoChanged(callbackInfo string) {
+
+}
+
+func (testFriendshipListener) OnSuccess() {
+
+}
+
+func (testFriendshipListener) OnError(code int32, msg string) {
+
+}
+
+type testGroupListener struct {
+}
+
+func (testGroupListener) OnJoinedGroupAdded(callbackInfo string) {
+
+}
+func (testGroupListener) OnJoinedGroupDeleted(callbackInfo string) {
+
+}
+
+func (testGroupListener) OnGroupMemberAdded(callbackInfo string) {
+
+}
+func (testGroupListener) OnGroupMemberDeleted(callbackInfo string) {
+
+}
+
+func (testGroupListener) OnGroupApplicationAdded(callbackInfo string) {
+
+}
+func (testGroupListener) OnGroupApplicationDeleted(callbackInfo string) {
+
+}
+
+func (testGroupListener) OnGroupInfoChanged(callbackInfo string) {
+
+}
+func (testGroupListener) OnGroupMemberInfoChanged(callbackInfo string) {
+
+}
+
+func (testGroupListener) OnGroupApplicationAccepted(callbackInfo string) {
+
+}
+func (testGroupListener) OnGroupApplicationRejected(callbackInfo string) {
+
+}
+
+func (testGroupListener) OnGroupDismissed(callbackInfo string) {
+
+}
+
+type testConnListener struct {
+ UserID string
+}
+
+func (t *testConnListener) OnUserTokenInvalid(errMsg string) {
+ log.ZError(context.TODO(), "user token invalid", errs.New("user token invalid").Wrap(), "userID", t.UserID)
+}
+
+func (t *testConnListener) OnUserTokenExpired() {
+ log.ZError(context.TODO(), "user token expired", errs.New("user token expired").Wrap(), "userID", t.UserID)
+}
+func (t *testConnListener) OnConnecting() {
+
+}
+
+func (t *testConnListener) OnConnectSuccess() {
+ vars.NowLoginNum.Add(1)
+}
+
+func (t *testConnListener) OnConnectFailed(ErrCode int32, ErrMsg string) {
+ log.ZError(context.TODO(), "connect failed", errs.NewCodeError(int(ErrCode), ErrMsg), "userID", t.UserID)
+}
+
+func (t *testConnListener) OnKickedOffline() {
+ log.ZError(context.TODO(), "kicked offline", errs.New("kicked offline").Wrap(), "userID", t.UserID)
+}
+
+func (t *testConnListener) OnSelfInfoUpdated(info string) {
+
+}
+func (t *testConnListener) OnUserCommandAdd(info string) {
+
+}
+func (t *testConnListener) OnUserCommandUpdate(info string) {
+
+}
+func (t *testConnListener) OnUserCommandDelete(info string) {
+
+}
+func (t *testConnListener) OnSuccess() {
+
+}
+
+func (t *testConnListener) OnError(code int32, msg string) {
+
+}
diff --git a/integration_test/internal/pkg/sdk_user_simulator/user.go b/integration_test/internal/pkg/sdk_user_simulator/user.go
new file mode 100644
index 000000000..6907987f4
--- /dev/null
+++ b/integration_test/internal/pkg/sdk_user_simulator/user.go
@@ -0,0 +1,53 @@
+package sdk_user_simulator
+
+import (
+ "sync"
+
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/tools/errs"
+)
+
+var (
+ MapLock sync.Mutex
+ UserMessageMap = make(map[string]*MsgListenerCallBak)
+ timeOffset int64
+)
+
+func GetRelativeServerTime() int64 {
+ return utils.GetCurrentTimestampByMill() + timeOffset
+}
+
+func InitSDK(userID string, cf sdk_struct.IMConfig) (*open_im_sdk.LoginMgr, error) {
+ userForSDK := open_im_sdk.NewLoginMgr()
+ var testConnListener testConnListener
+ testConnListener.UserID = userID
+ isInit := userForSDK.InitSDK(cf, &testConnListener)
+ if !isInit {
+ return nil, errs.New("sdk init failed").Wrap()
+ }
+
+ SetListener(userForSDK, userID)
+
+ return userForSDK, nil
+}
+
+func SetListener(userForSDK *open_im_sdk.LoginMgr, userID string) {
+ var testConversation conversationCallBack
+ userForSDK.SetConversationListener(&testConversation)
+ var testUser userCallback
+ userForSDK.SetUserListener(testUser)
+
+ msgCallBack := NewMsgListenerCallBak(userID)
+ MapLock.Lock()
+ UserMessageMap[userID] = msgCallBack
+ MapLock.Unlock()
+ userForSDK.SetAdvancedMsgListener(msgCallBack)
+
+ var friendshipListener testFriendshipListener
+ userForSDK.SetFriendshipListener(friendshipListener)
+
+ var groupListener testGroupListener
+ userForSDK.SetGroupListener(groupListener)
+}
diff --git a/integration_test/internal/pkg/utils/context.go b/integration_test/internal/pkg/utils/context.go
new file mode 100644
index 000000000..7a718f94b
--- /dev/null
+++ b/integration_test/internal/pkg/utils/context.go
@@ -0,0 +1,13 @@
+package utils
+
+import "context"
+
+func CancelAndReBuildCtx(
+ buildFunc func(ctx context.Context) context.Context,
+ cancel ...context.CancelFunc,
+) context.Context {
+ for _, cf := range cancel {
+ cf()
+ }
+ return buildFunc(nil)
+}
diff --git a/integration_test/internal/pkg/utils/err_test.go b/integration_test/internal/pkg/utils/err_test.go
new file mode 100644
index 000000000..0c73f1333
--- /dev/null
+++ b/integration_test/internal/pkg/utils/err_test.go
@@ -0,0 +1,33 @@
+package utils
+
+import (
+ "fmt"
+ "github.com/openimsdk/tools/errs"
+ "testing"
+)
+
+func TestErr(t *testing.T) {
+ err := A5()
+ fmt.Println(FormatErrorStack(err))
+}
+
+func A1() error {
+ err := errs.New("err1").Wrap()
+ return err
+}
+
+func A2() error {
+ return A1()
+}
+
+func A3() error {
+ return A2()
+}
+
+func A4() error {
+ return A3()
+}
+
+func A5() error {
+ return A4()
+}
diff --git a/integration_test/internal/pkg/utils/error.go b/integration_test/internal/pkg/utils/error.go
new file mode 100644
index 000000000..ed3c25e20
--- /dev/null
+++ b/integration_test/internal/pkg/utils/error.go
@@ -0,0 +1,21 @@
+package utils
+
+import (
+ "fmt"
+ "strings"
+)
+
+func FormatErrorStack(err error) string {
+ if err == nil {
+ return ""
+ }
+
+ var sb strings.Builder
+ sb.WriteString(fmt.Sprintf("%+v", err))
+
+ stack := sb.String()
+ stack = strings.ReplaceAll(stack, "\n", " => ")
+ stack = strings.ReplaceAll(stack, "\t", " ")
+
+ return stack
+}
diff --git a/integration_test/internal/pkg/utils/group.go b/integration_test/internal/pkg/utils/group.go
new file mode 100644
index 000000000..c1da0e2e1
--- /dev/null
+++ b/integration_test/internal/pkg/utils/group.go
@@ -0,0 +1,7 @@
+package utils
+
+import "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+
+func BuildGroupName(ownerID, type_ string) string {
+ return vars.GroupNamePrefix + type_ + "_" + ownerID
+}
diff --git a/integration_test/internal/pkg/utils/user.go b/integration_test/internal/pkg/utils/user.go
new file mode 100644
index 000000000..f0c6bb444
--- /dev/null
+++ b/integration_test/internal/pkg/utils/user.go
@@ -0,0 +1,108 @@
+package utils
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "strconv"
+ "strings"
+)
+
+func GenUserIDs(num int) []string {
+ ids := make([]string, num)
+ for i := 0; i < num; i++ {
+ ids[i] = GetUserID(i)
+ }
+ return ids
+}
+
+func GetUserID(num int) string {
+ return vars.UserIDPrefix + strconv.Itoa(num)
+}
+
+func GetUserNum(id string) (int, error) {
+ if !strings.HasPrefix(id, vars.UserIDPrefix) {
+ return -1, errs.New("invalid user id in GetUserNum").Wrap()
+ }
+ num, err := strconv.Atoi(strings.TrimPrefix(id, vars.UserIDPrefix))
+ if err != nil {
+ return -1, errs.WrapMsg(err, "invalid user id in GetUserNum")
+ }
+ return num, nil
+}
+
+func MustGetUserNum(id string) int {
+ num, err := GetUserNum(id)
+ if err != nil {
+ log.ZError(context.TODO(), "err in MustGetUserNum", err)
+ }
+ return num
+}
+
+// IsSuperUser check if the user has all friends
+func IsSuperUser(id string) bool {
+ num := MustGetUserNum(id)
+ return datautil.BetweenLEq(num, 0, vars.SuperUserNum)
+}
+
+// IsLogin check if the user is login
+func IsLogin(id string) bool {
+ num := MustGetUserNum(id)
+ return IsNumLogin(num)
+}
+
+// IsNumLogin check if the user is login
+func IsNumLogin(num int) bool {
+ return datautil.BetweenLEq(num, 0, vars.LoginUserNum)
+}
+
+// NextOffsetNums get num with an offset behind.
+func NextOffsetNums(userNum, offset int) []int {
+ ids := make([]int, offset)
+ for i := 1; i <= offset; i++ {
+ ids[i-1] = NextOffsetNum(userNum, i)
+ }
+ return ids
+}
+
+// NextOffsetNum get num with an offset behind.
+func NextOffsetNum(num, offset int) int {
+ offset = offset % vars.UserNum
+ return (num + offset + vars.UserNum) % vars.UserNum
+}
+
+// NextNum get next num.
+func NextNum(num int) int {
+ return NextOffsetNum(num, 1)
+}
+
+// NextOffsetUserIDs get userIDs with an offset behind.
+func NextOffsetUserIDs(userNum, offset int) []string {
+ ids := make([]string, offset)
+ for i := 1; i <= offset; i++ {
+ ids[i-1] = GetUserID(NextOffsetNum(userNum, i))
+ }
+ return ids
+}
+
+// NextLoginOffsetNum get num with an offset behind.
+func NextLoginOffsetNum(num, offset int) int {
+ offset = offset % vars.LoginUserNum
+ return (num + offset + vars.LoginUserNum) % vars.LoginUserNum
+}
+
+// NextLoginNum get next num.
+func NextLoginNum(num int) int {
+ return NextLoginOffsetNum(num, 1)
+}
+
+// NextLoginOffsetUserIDs get userIDs with an offset behind.
+func NextLoginOffsetUserIDs(userNum, offset int) []string {
+ ids := make([]string, offset)
+ for i := 1; i <= offset; i++ {
+ ids[i-1] = GetUserID(NextOffsetNum(userNum, i))
+ }
+ return ids
+}
diff --git a/integration_test/internal/process/process.go b/integration_test/internal/process/process.go
new file mode 100644
index 000000000..53555055e
--- /dev/null
+++ b/integration_test/internal/process/process.go
@@ -0,0 +1,215 @@
+package process
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "reflect"
+ "runtime"
+ "runtime/debug"
+)
+
+type Process struct {
+ ctx context.Context
+ Tasks []*Task
+
+ // Only all RunCondition is true, process will exec tasks.
+ RunConditions []bool
+
+ nowTaskNum int // now un exec task number
+}
+
+func NewProcess() *Process {
+ return &Process{}
+}
+
+func (p *Process) GetTaskNum() int {
+ return len(p.Tasks)
+}
+
+func (p *Process) AddTasks(task ...*Task) {
+ if len(task) != 0 {
+ p.Tasks = append(p.Tasks, task...)
+ }
+}
+
+func (p *Process) AddConditions(condition ...bool) {
+ if len(condition) != 0 {
+ p.RunConditions = append(p.RunConditions, condition...)
+ }
+}
+
+func (p *Process) ResetConditions(condition ...bool) {
+ p.RunConditions = condition
+}
+
+func (p *Process) Exec() error {
+ return p.ExecOffset(0)
+}
+
+func (p *Process) ContinueExec() error {
+ return p.ExecOffset(p.nowTaskNum)
+}
+
+func (p *Process) ExecOffset(offset int) error {
+ if offset < 0 || offset > len(p.Tasks) {
+ return errs.New("err input offset is process exec").Wrap()
+ }
+ var (
+ interrupt = -1
+ )
+
+ return p.execTasks(offset, interrupt)
+}
+
+func (p *Process) execTasks(offset, interrupt int) error {
+ p.nowTaskNum = offset
+ for _, task := range p.Tasks[offset:] {
+ if p.nowTaskNum == interrupt {
+ return nil
+ }
+ if p.shouldRun() && task.ShouldRun {
+ if task.ShouldRun {
+ if err := p.call(task.Func, task.Args...); err != nil {
+ return err
+ }
+ } else {
+ if err := p.call(task.NegativeFunc, task.NegativeArgs...); err != nil {
+ return err
+ }
+ }
+ }
+ p.nowTaskNum++
+ }
+ return nil
+}
+
+func (p *Process) SetContext(ctx context.Context) {
+ p.ctx = ctx
+}
+
+func (p *Process) shouldRun() bool {
+ if len(p.RunConditions) == 0 {
+ return true
+ }
+ for _, cond := range p.RunConditions {
+ if !cond {
+ return false
+ }
+ }
+ return true
+}
+
+func (p *Process) call(fn any, args ...any) (err error) {
+ funcPtr := reflect.ValueOf(fn).Pointer()
+ funcName := runtime.FuncForPC(funcPtr).Name()
+ defer func() {
+ ctx := context.TODO()
+ if r := recover(); r != nil {
+ fmt.Printf("panic: %+v\n%s", r, debug.Stack())
+ err = fmt.Errorf("call panic: %+v", r)
+ } else {
+ if err == nil {
+ log.ZInfo(ctx, "fn call success", "function name", funcName)
+ } else {
+ log.ZError(ctx, "fn call error", err, "function name", funcName)
+ }
+ }
+ }()
+
+ fnv := reflect.ValueOf(fn)
+ if fnv.Kind() != reflect.Func {
+ return errs.New("call input type is not func").Wrap()
+ }
+ if fnv.IsNil() {
+ return nil
+ }
+
+ fnt := fnv.Type()
+ nin := fnt.NumIn()
+ var (
+ ins = make([]reflect.Value, 0, nin)
+ fieldOffset = 0 // if have ctx, parse field offset = 1
+ isVariadic = fnt.IsVariadic()
+ )
+
+ if nin != 0 {
+ argsLen := len(args)
+ // If there are parameters, the first parameter must be ctx
+ if fnt.In(0).Implements(reflect.ValueOf(new(context.Context)).Elem().Type()) {
+ fieldOffset = 1
+ ins = append(ins, reflect.ValueOf(p.ctx))
+ argsLen++
+ }
+ if isVariadic {
+ nin--
+ }
+ if (isVariadic && argsLen < nin) || (!isVariadic && argsLen != nin) {
+ return errs.New(fmt.Sprintf("call input args num not correct. nin: %d, argsLen: %d", nin, args)).Wrap()
+ }
+ }
+
+ for i := 0; i < len(args); i++ {
+ inFnField := fnt.In(i + fieldOffset)
+
+ arg := reflect.TypeOf(args[i])
+
+ var expectedType reflect.Type
+ if isVariadic && i+fieldOffset >= nin {
+ expectedType = fnt.In(nin).Elem()
+ } else {
+ expectedType = fnt.In(i + fieldOffset)
+ }
+
+ if arg.AssignableTo(expectedType) {
+ ins = append(ins, reflect.ValueOf(args[i]))
+ continue
+ }
+ if arg.Kind() == reflect.String { // json
+ var ptr int
+ for inFnField.Kind() == reflect.Ptr {
+ inFnField = inFnField.Elem()
+ ptr++
+ }
+ switch inFnField.Kind() {
+ case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
+ v := reflect.New(inFnField)
+ if err := json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil {
+ return errs.New(fmt.Sprintf("go call json.Unmarshal error: %s", err)).Wrap()
+ }
+ if ptr == 0 {
+ v = v.Elem()
+ } else if ptr != 1 {
+ for i := ptr - 1; i > 0; i-- {
+ temp := reflect.New(v.Type())
+ temp.Elem().Set(v)
+ v = temp
+ }
+ }
+ ins = append(ins, v)
+ continue
+ }
+ }
+ return errs.New(fmt.Sprintf("go code error: fn in args type is not match. index:%d, field type:%s, arg type:%s",
+ i, inFnField.String(), arg.String())).Wrap()
+ }
+ outs := fnv.Call(ins)
+ if len(outs) == 0 {
+ return nil
+ }
+ if fnt.Out(len(outs) - 1).Implements(reflect.ValueOf(new(error)).Elem().Type()) {
+ if errValueOf := outs[len(outs)-1]; !errValueOf.IsNil() {
+ return errValueOf.Interface().(error)
+ }
+ }
+
+ return nil
+}
+
+func (p *Process) Clear() {
+ p.Tasks = nil
+ p.RunConditions = nil
+ p.nowTaskNum = 0
+}
diff --git a/integration_test/internal/process/process_helper.go b/integration_test/internal/process/process_helper.go
new file mode 100644
index 000000000..3c493562c
--- /dev/null
+++ b/integration_test/internal/process/process_helper.go
@@ -0,0 +1,5 @@
+package process
+
+func AddConditions(p *Process, condition ...bool) {
+ p.RunConditions = append(p.RunConditions, condition...)
+}
diff --git a/integration_test/internal/process/task.go b/integration_test/internal/process/task.go
new file mode 100644
index 000000000..9ec1d85e5
--- /dev/null
+++ b/integration_test/internal/process/task.go
@@ -0,0 +1,23 @@
+package process
+
+type Task struct {
+ ShouldRun bool // determine if task will run
+ Func any // must be func. run funcs
+ Args []any // func args
+ NegativeFunc any // must be func. if !ShouldRun, will run this
+ NegativeArgs []any // negative args
+}
+
+func NewTask(shouldRun bool, f any, args ...any) *Task {
+ return &Task{
+ ShouldRun: shouldRun,
+ Func: f,
+ Args: args,
+ }
+}
+
+func (t *Task) AddNegativeFunc(f any, args ...any) *Task {
+ t.NegativeFunc = f
+ t.NegativeArgs = args
+ return t
+}
diff --git a/integration_test/internal/sdk/conversation.go b/integration_test/internal/sdk/conversation.go
new file mode 100644
index 000000000..2df620aec
--- /dev/null
+++ b/integration_test/internal/sdk/conversation.go
@@ -0,0 +1,13 @@
+package sdk
+
+import (
+ "context"
+)
+
+func (s *TestSDK) GetTotalConversationCount(ctx context.Context) (int, error) {
+ res, err := s.SDK.Conversation().GetAllConversationList(ctx)
+ if err != nil {
+ return 0, err
+ }
+ return len(res), nil
+}
diff --git a/integration_test/internal/sdk/group.go b/integration_test/internal/sdk/group.go
new file mode 100644
index 000000000..5bd5f0403
--- /dev/null
+++ b/integration_test/internal/sdk/group.go
@@ -0,0 +1,85 @@
+package sdk
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ sdkUtils "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "time"
+)
+
+// CreateCommonGroup create a regular group. Group members are the users with IDs
+// starting from the current user's ID up to the next memberNum users.
+func (s *TestSDK) CreateCommonGroup(ctx context.Context, memberNum int) (*sdkws.GroupInfo, error) {
+ memberUserIds := utils.NextOffsetUserIDs(s.Num, memberNum-1) // 1 is oneself
+ resp, err := s.createGroup(ctx, memberUserIds, vars.CommonGroup)
+ if err != nil {
+ return nil, err
+ }
+ return resp, nil
+}
+
+// CreateLargeGroup create a large group. Group members are all users.
+func (s *TestSDK) CreateLargeGroup(ctx context.Context) (*sdkws.GroupInfo, error) {
+ memberUserIDs := datautil.Delete(utils.GenUserIDs(vars.LargeGroupMemberNum), utils.MustGetUserNum(s.UserID))
+ resp, err := s.createGroup(ctx, memberUserIDs, vars.LargeGroup)
+ if err != nil {
+ return nil, err
+ }
+ return resp, nil
+}
+
+func (s *TestSDK) createGroup(ctx context.Context, memberUserIds []string, groupType string) (*sdkws.GroupInfo, error) {
+ initialMembers := memberUserIds
+ if len(memberUserIds) > 1000 {
+ initialMembers = memberUserIds[:1000]
+ }
+
+ g, err := s.SDK.Group().CreateGroup(ctx, &group.CreateGroupReq{
+ MemberUserIDs: initialMembers,
+ GroupInfo: &sdkws.GroupInfo{
+ GroupName: utils.BuildGroupName(s.UserID, groupType),
+ GroupType: constant.WorkingGroup,
+ },
+ AdminUserIDs: nil,
+ OwnerUserID: s.UserID,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ if len(memberUserIds) > 1000 {
+ for i := 1000; i < len(memberUserIds); i += config.ApiParamLength {
+ end := i + config.ApiParamLength
+ if end > len(memberUserIds) {
+ end = len(memberUserIds)
+ }
+ t := time.Now()
+ ctx = ccontext.WithOperationID(ctx, sdkUtils.OperationIDGenerator())
+ log.ZWarn(ctx, "InviteUserToGroup begin", nil, "begin", i, "end", end, "groupID", g.GroupID)
+ err := s.SDK.Group().InviteUserToGroup(ctx, g.GroupID, "", memberUserIds[i:end])
+ if err != nil {
+ return nil, err
+ }
+ log.ZWarn(ctx, "InviteUserToGroup end", nil, "begin", i, "end", end, "groupID", g.GroupID, "cost time", time.Since(t))
+ time.Sleep(time.Second)
+ }
+ }
+
+ return g, nil
+}
+
+func (s *TestSDK) GetAllJoinedGroups(ctx context.Context) ([]*sdkws.GroupInfo, int, error) {
+ res, err := s.SDK.Group().GetServerJoinGroup(ctx)
+ if err != nil {
+ return nil, 0, err
+ }
+ return res, len(res), err
+}
diff --git a/integration_test/internal/sdk/msg.go b/integration_test/internal/sdk/msg.go
new file mode 100644
index 000000000..752a0a967
--- /dev/null
+++ b/integration_test/internal/sdk/msg.go
@@ -0,0 +1,25 @@
+package sdk
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+)
+
+func (s *TestSDK) SendSingleMsg(ctx context.Context, msg *sdk_struct.MsgStruct, receiveID string) (*sdk_struct.MsgStruct, error) {
+ vars.SendMsgCount.Add(1)
+ res, err := s.SDK.Conversation().SendMessage(ctx, msg, receiveID, "", nil, false)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+func (s *TestSDK) SendGroupMsg(ctx context.Context, msg *sdk_struct.MsgStruct, groupID string) (*sdk_struct.MsgStruct, error) {
+ vars.SendMsgCount.Add(1)
+ res, err := s.SDK.Conversation().SendMessage(ctx, msg, "", groupID, nil, false)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+}
diff --git a/integration_test/internal/sdk/relation.go b/integration_test/internal/sdk/relation.go
new file mode 100644
index 000000000..cacf7c158
--- /dev/null
+++ b/integration_test/internal/sdk/relation.go
@@ -0,0 +1,17 @@
+package sdk
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+)
+
+func (s *TestSDK) GetAllFriends(ctx context.Context) ([]*model_struct.LocalFriend, error) {
+ res, err := s.SDK.Relation().GetFriendList(ctx, false)
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+
+}
diff --git a/integration_test/internal/sdk/sdk.go b/integration_test/internal/sdk/sdk.go
new file mode 100644
index 000000000..232f73238
--- /dev/null
+++ b/integration_test/internal/sdk/sdk.go
@@ -0,0 +1,24 @@
+package sdk
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+)
+
+var (
+ // TestSDKs SDK slice. Index is user num
+ TestSDKs []*TestSDK
+)
+
+type TestSDK struct {
+ UserID string
+ Num int
+ SDK *open_im_sdk.LoginMgr
+}
+
+func NewTestSDK(userID string, num int, loginMgr *open_im_sdk.LoginMgr) *TestSDK {
+ return &TestSDK{
+ UserID: userID,
+ Num: num,
+ SDK: loginMgr,
+ }
+}
diff --git a/integration_test/internal/statistics/msg.go b/integration_test/internal/statistics/msg.go
new file mode 100644
index 000000000..96c9921f2
--- /dev/null
+++ b/integration_test/internal/statistics/msg.go
@@ -0,0 +1,80 @@
+package statistics
+
+import (
+ "context"
+ "fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/decorator"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/tools/log"
+ "strings"
+ "time"
+)
+
+func MsgConsuming(ctx context.Context) {
+ defer decorator.FuncLog(ctx)()
+ time.Sleep(time.Second * 5)
+ close(vars.RecvMsgConsuming)
+ var (
+ low int
+ mid int
+ high int
+ outHigh int
+ minT *vars.StatMsg
+ maxT *vars.StatMsg
+ totalCost int64
+ count int
+ )
+ for msg := range vars.RecvMsgConsuming {
+
+ sec := msg.CostTime
+ switch {
+ case sec < config.ReceiveMsgTimeThresholdLow*1000:
+ low++
+ case sec < config.ReceiveMsgTimeThresholdMedium*1000:
+ mid++
+ case sec < config.ReceiveMsgTimeThresholdHigh*1000:
+ high++
+ default:
+ outHigh++
+ }
+
+ if minT == nil || minT.CostTime > sec {
+ minT = msg
+ }
+ if maxT == nil || maxT.CostTime < sec {
+ maxT = msg
+ }
+
+ totalCost += sec
+ count++
+ }
+
+ if minT == nil || maxT == nil {
+ return
+ }
+ statStr := `
+statistic msg count: %d
+statistic send msg count: %d
+receive msg in %d s count: %d
+receive msg in %d s count: %d
+receive msg in %d s count: %d
+receive messages within %d s or more: %d
+maximum time to receive messages: %d ms, create: %d, receive: %d, msg: %v
+minimum time to receive messages: %d ms, create: %d, receive: %d, msg: %v
+average time consuming: %.2f ms
+`
+ statStr = fmt.Sprintf(statStr,
+ count,
+ vars.SendMsgCount.Load(),
+ config.ReceiveMsgTimeThresholdLow, low,
+ config.ReceiveMsgTimeThresholdMedium, mid,
+ config.ReceiveMsgTimeThresholdHigh, high,
+ config.ReceiveMsgTimeThresholdHigh, outHigh,
+ maxT.CostTime, maxT.Msg.CreateTime, maxT.ReceiveTime, *maxT.Msg,
+ minT.CostTime, minT.Msg.CreateTime, minT.ReceiveTime, *minT.Msg,
+ float64(totalCost)/float64(count))
+
+ fmt.Println(statStr)
+ log.ZInfo(ctx, "stat msg consuming", "res", strings.Replace(statStr, "\n", "; ", -1))
+}
diff --git a/integration_test/internal/vars/cmd.go b/integration_test/internal/vars/cmd.go
new file mode 100644
index 000000000..47d2d416a
--- /dev/null
+++ b/integration_test/internal/vars/cmd.go
@@ -0,0 +1,51 @@
+package vars
+
+var (
+ UserNum int // user num
+ SuperUserNum int // number of users with all friends
+ LargeGroupNum int // number of large group
+ LargeGroupMemberNum int // number of large group member num
+ CommonGroupNum int // number of common group create by each user
+ CommonGroupMemberNum int // number of common group member num
+ SingleMessageNum int // number of single message each user send
+ GroupMessageNum int // number of group message each user send
+
+ ShouldRegister bool // determine whether register
+ ShouldImportFriends bool // determine whether import friends
+ ShouldCreateGroup bool // determine whether create group
+ ShouldSendMsg bool // determine whether send messages
+
+ ShouldCheckGroupNum bool // determine whether check group num
+ ShouldCheckConversationNum bool // determine whether check conversation num
+ ShouldCheckMessageNum bool // determine whether check message num
+ ShouldCheckUninsAndReins bool // determine whether check again after uninstall and reinstall
+
+ LoginRate float64 // number of login user rate
+)
+
+var (
+ FlagMap = map[string]string{
+ "TestMode": "test",
+ "UserNum": "u",
+ "SuperUserNum": "su",
+ "LargeGroupNum": "lg",
+ "LargeGroupMemberNum": "lgm",
+ "CommonGroupNum": "cg",
+ "CommonGroupMemberNum": "cgm",
+ "SingleMessageNum": "sm",
+ "GroupMessageNum": "gm",
+ "ShouldRegister": "reg",
+ "ShouldImportFriends": "imf",
+ "ShouldCreateGroup": "crg",
+ "ShouldSendMsg": "sem",
+ "ShouldCheckGroupNum": "ckgn",
+ "ShouldCheckConversationNum": "ckcon",
+ "ShouldCheckMessageNum": "ckmsn",
+ "ShouldCheckUninsAndReins": "ckuni",
+ "LoginRate": "lgr",
+ }
+)
+
+var (
+ IsLogin = false
+)
diff --git a/integration_test/internal/vars/group.go b/integration_test/internal/vars/group.go
new file mode 100644
index 000000000..fe554cb58
--- /dev/null
+++ b/integration_test/internal/vars/group.go
@@ -0,0 +1,10 @@
+package vars
+
+const (
+ GroupNamePrefix = "group_test_"
+)
+
+const (
+ CommonGroup = "common"
+ LargeGroup = "large"
+)
diff --git a/integration_test/internal/vars/msg.go b/integration_test/internal/vars/msg.go
new file mode 100644
index 000000000..d2ff2cac6
--- /dev/null
+++ b/integration_test/internal/vars/msg.go
@@ -0,0 +1,17 @@
+package vars
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "sync/atomic"
+)
+
+type StatMsg struct {
+ CostTime int64
+ ReceiveTime int64
+ Msg *sdk_struct.MsgStruct
+}
+
+var (
+ SendMsgCount atomic.Int64
+ RecvMsgConsuming chan *StatMsg
+)
diff --git a/integration_test/internal/vars/os.go b/integration_test/internal/vars/os.go
new file mode 100644
index 000000000..fbdb3d71c
--- /dev/null
+++ b/integration_test/internal/vars/os.go
@@ -0,0 +1,7 @@
+package vars
+
+import "os"
+
+var (
+ OsStdout = os.Stdout
+)
diff --git a/integration_test/internal/vars/user.go b/integration_test/internal/vars/user.go
new file mode 100644
index 000000000..f93bd82fc
--- /dev/null
+++ b/integration_test/internal/vars/user.go
@@ -0,0 +1,21 @@
+package vars
+
+import (
+ "context"
+ "sync/atomic"
+)
+
+const (
+ UserIDPrefix = "test_v3_u"
+)
+
+var (
+ UserIDs []string // all user ids
+ SuperUserIDs []string // user ids of users with all friends
+
+ Contexts []context.Context // users contexts
+ Cancels []context.CancelFunc // users contexts
+
+ LoginUserNum int
+ NowLoginNum atomic.Int64
+)
diff --git a/integration_test/main.go b/integration_test/main.go
new file mode 100644
index 000000000..2c0ce618c
--- /dev/null
+++ b/integration_test/main.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/checker"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/config"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/manager"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/initialization"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/process"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/statistics"
+ "github.com/openimsdk/openim-sdk-core/v3/integration_test/internal/vars"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/formatutil"
+ "os"
+ "runtime/debug"
+ "time"
+)
+
+const runFailed = -1
+
+func Init(ctx context.Context) error {
+ initialization.InitFlag()
+ flag.Parse()
+ initialization.SetFlagLimit()
+ initialization.GenUserIDs()
+ initialization.InitGlobalData()
+ if err := initialization.InitLog(config.GetConf()); err != nil {
+ return err
+ }
+ initialization.PrintFlag()
+
+ return nil
+}
+
+func DoFlagFunc(ctx context.Context) (err error) {
+ defer func() {
+ // capture check err
+ ch := checker.CloseAndGetCheckErrChan()
+ for e := range ch {
+ if err == nil {
+ err = e
+ }
+ log.ZError(ctx, "check err", err)
+ }
+ }()
+ var (
+ pro = process.NewProcess()
+
+ m = manager.NewMetaManager()
+ userMng = manager.NewUserManager(m)
+ groupMng = manager.NewGroupManager(m)
+ relationMng = manager.NewRelationManager(m)
+ msgMng = manager.NewMsgManager(m)
+ fileMng = manager.NewFileManager(m)
+ )
+ if err = m.WithAdminToken(); err != nil {
+ return err
+ }
+ ctx = m.BuildCtx(ctx)
+
+ pro.SetContext(ctx)
+
+ checkTasks := []*process.Task{
+ process.NewTask(vars.ShouldCheckGroupNum, checker.CheckGroupNum),
+ process.NewTask(vars.ShouldCheckConversationNum, checker.CheckConvNumAfterImpFriAndCrGro),
+ process.NewTask(vars.ShouldCheckMessageNum, checker.CheckMessageNum),
+ }
+
+ pro.AddTasks(
+ process.NewTask(vars.ShouldRegister, userMng.RegisterAllUsers),
+ process.NewTask(true, userMng.InitAllSDK),
+ process.NewTask(vars.IsLogin, userMng.LoginByRate),
+ process.NewTask(vars.IsLogin, checker.CheckLoginByRateNum),
+
+ process.NewTask(vars.ShouldImportFriends, relationMng.ImportFriends),
+ process.NewTask(vars.ShouldImportFriends, checker.CheckLoginUsersFriends),
+
+ process.NewTask(vars.ShouldCreateGroup, groupMng.CreateGroups),
+ process.NewTask(vars.ShouldSendMsg, msgMng.SendMessages),
+ //process.NewTask(vars.ShouldSendMsg, Sleep),
+
+ process.NewTask(vars.IsLogin, userMng.LoginLastUsers),
+ process.NewTask(vars.IsLogin, checker.CheckAllLoginNum),
+ )
+
+ pro.AddTasks(checkTasks...)
+ pro.AddTasks(process.NewTask(vars.ShouldCheckMessageNum, statistics.MsgConsuming))
+
+ // Uninstall and reinstall
+ pro.AddTasks(
+ process.NewTask(true, process.AddConditions, pro, vars.ShouldCheckUninsAndReins),
+ process.NewTask(true, fileMng.DeleteLocalDB),
+ process.NewTask(true, userMng.ForceLogoutAllUsers),
+ process.NewTask(true, userMng.InitAllSDK),
+ process.NewTask(true, userMng.LoginAllUsers),
+ )
+ pro.AddTasks(checkTasks...)
+
+ return pro.Exec()
+}
+
+func main() {
+ var err error
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Printf("panic: %v\n", r)
+ fmt.Println("Stack trace:")
+ fmt.Printf("%s", debug.Stack())
+ os.Exit(runFailed)
+ }
+ if err != nil {
+ fmt.Println(utils.FormatErrorStack(err))
+ os.Exit(runFailed)
+ }
+ }()
+ ctx := context.Background()
+ if err = Init(ctx); err != nil {
+ log.ZError(ctx, "init err", err, "stack", utils.FormatErrorStack(err))
+ fmt.Println("init err")
+ return
+ }
+ if err = DoFlagFunc(ctx); err != nil {
+ log.ZError(ctx, "do flag err", err, "stack", utils.FormatErrorStack(err))
+ fmt.Println("do flag err")
+ return
+ }
+
+ log.ZInfo(ctx, "start success!")
+ fmt.Println("start success!")
+}
+
+func Sleep() {
+ fmt.Printf("sleep %d s for sync data~\n", config.SleepSec)
+ fmt.Print(formatutil.ProgressBar("Sleep", 0, config.SleepSec))
+ for i := 0; i < config.SleepSec; i++ {
+ fmt.Print(formatutil.ProgressBar("Sleep", i+1, config.SleepSec))
+ time.Sleep(time.Second)
+ }
+ fmt.Print("\n")
+}
diff --git a/internal/business/business.go b/internal/business/business.go
deleted file mode 100644
index ecc02cec9..000000000
--- a/internal/business/business.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package business
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
-
- "github.com/openimsdk/protocol/sdkws"
-
- "github.com/openimsdk/tools/log"
-)
-
-type Business struct {
- listener func() open_im_sdk_callback.OnCustomBusinessListener
- db db_interface.DataBase
-}
-
-func NewBusiness(db db_interface.DataBase) *Business {
- return &Business{
- db: db,
- }
-}
-
-func (b *Business) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
- var n sdk_struct.NotificationElem
- err := utils.JsonStringToStruct(string(msg.Content), &n)
- if err != nil {
- log.ZError(ctx, "unmarshal failed", err, "msg", msg)
- return
-
- }
- b.listener().OnRecvCustomBusinessMessage(n.Detail)
-}
diff --git a/internal/business/open_im_sdk_business.go b/internal/business/open_im_sdk_business.go
deleted file mode 100644
index 79527c117..000000000
--- a/internal/business/open_im_sdk_business.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package business
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
-)
-
-func (w *Business) SetListener(listener func() open_im_sdk_callback.OnCustomBusinessListener) {
- w.listener = listener
-}
diff --git a/internal/common/common.go b/internal/common/common.go
deleted file mode 100644
index 76250b5c0..000000000
--- a/internal/common/common.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package common
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
-
- "github.com/golang/protobuf/proto"
- "github.com/openimsdk/protocol/sdkws"
-)
-
-func UnmarshalTips(msg *sdkws.MsgData, detail proto.Message) error {
- var tips sdkws.TipsComm
- if err := proto.Unmarshal(msg.Content, &tips); err != nil {
- return utils.Wrap(err, "")
- }
- if err := proto.Unmarshal(tips.Detail, detail); err != nil {
- return utils.Wrap(err, "")
- }
- return nil
-}
diff --git a/internal/common/object_storage.go b/internal/common/object_storage.go
deleted file mode 100644
index 523ccfb33..000000000
--- a/internal/common/object_storage.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package common
-
-import (
- "bytes"
- _ "image/gif"
- _ "image/jpeg"
- _ "image/png"
-)
-
-type ObjectStorage interface {
- UploadImage(filePath string, onProgressFun func(int)) (string, string, error)
- UploadSound(filePath string, onProgressFun func(int)) (string, string, error)
- UploadFile(filePath string, onProgressFun func(int)) (string, string, error)
- UploadVideo(videoPath, snapshotPath string, onProgressFun func(int)) (string, string, string, string, error)
- UploadImageByBuffer(buffer *bytes.Buffer, size int64, imageType string, onProgressFun func(int)) (string, string, error)
- UploadSoundByBuffer(buffer *bytes.Buffer, size int64, fileType string, onProgressFun func(int)) (string, string, error)
- UploadFileByBuffer(buffer *bytes.Buffer, size int64, fileType string, onProgressFun func(int)) (string, string, error)
- UploadVideoByBuffer(videoBuffer, snapshotBuffer *bytes.Buffer, videoSize, snapshotSize int64, videoType, snapshotType string, onProgressFun func(int)) (string, string, string, string, error)
-}
diff --git a/internal/conversation_msg/sdk.go b/internal/conversation_msg/api.go
similarity index 70%
rename from internal/conversation_msg/sdk.go
rename to internal/conversation_msg/api.go
index 103849b7d..246c937f3 100644
--- a/internal/conversation_msg/sdk.go
+++ b/internal/conversation_msg/api.go
@@ -1,17 +1,3 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
package conversation_msg
import (
@@ -25,7 +11,11 @@ import (
"sync"
"time"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
+ pconstant "github.com/openimsdk/protocol/constant"
+
+ "github.com/openimsdk/tools/errs"
+
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
@@ -41,7 +31,6 @@ import (
pbConversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/protocol/wrapperspb"
"github.com/jinzhu/copier"
)
@@ -66,22 +55,6 @@ func (c *Conversation) GetAtAllTag(_ context.Context) string {
return constant.AtAllString
}
-// deprecated
-func (c *Conversation) GetConversationRecvMessageOpt(ctx context.Context, conversationIDs []string) (resp []*server_api_params.GetConversationRecvMessageOptResp, err error) {
- conversations, err := c.db.GetMultipleConversationDB(ctx, conversationIDs)
- if err != nil {
- return nil, err
- }
- for _, conversation := range conversations {
- resp = append(resp, &server_api_params.GetConversationRecvMessageOptResp{
- ConversationID: conversation.ConversationID,
- Result: &conversation.RecvMsgOpt,
- })
- }
- return resp, nil
-}
-
-// Method to set global message receiving options
func (c *Conversation) GetOneConversation(ctx context.Context, sessionType int32, sourceID string) (*model_struct.LocalConversation, error) {
conversationID := c.getConversationIDBySessionType(sourceID, int(sessionType))
lc, err := c.db.GetConversation(ctx, conversationID)
@@ -100,24 +73,20 @@ func (c *Conversation) GetOneConversation(ctx context.Context, sessionType int32
}
newConversation.ShowName = name
newConversation.FaceURL = faceUrl
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
newConversation.GroupID = sourceID
- g, err := c.full.GetGroupInfoFromLocal2Svr(ctx, sourceID, sessionType)
+ g, err := c.group.FetchGroupOrError(ctx, sourceID)
if err != nil {
return nil, err
}
newConversation.ShowName = g.GroupName
newConversation.FaceURL = g.FaceURL
}
- time.Sleep(time.Millisecond * 500)
- lc, errTemp := c.db.GetConversation(ctx, conversationID)
- if errTemp == nil {
+ //double check if the conversation exists
+ lc, err := c.db.GetConversation(ctx, conversationID)
+ if err == nil {
return lc, nil
}
- err := c.db.InsertConversation(ctx, &newConversation)
- if err != nil {
- return nil, err
- }
return &newConversation, nil
}
}
@@ -155,7 +124,10 @@ func (c *Conversation) SetConversationDraft(ctx context.Context, conversationID,
return nil
}
-func (c *Conversation) setConversationAndSync(ctx context.Context, conversationID string, req *pbConversation.ConversationReq) error {
+func (c *Conversation) SetConversation(ctx context.Context, conversationID string, req *pbConversation.ConversationReq) error {
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
+
lc, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
@@ -168,38 +140,6 @@ func (c *Conversation) setConversationAndSync(ctx context.Context, conversationI
return c.IncrSyncConversations(ctx)
}
-func (c *Conversation) ResetConversationGroupAtType(ctx context.Context, conversationID string) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{GroupAtType: &wrapperspb.Int32Value{Value: 0}})
-}
-
-func (c *Conversation) PinConversation(ctx context.Context, conversationID string, isPinned bool) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsPinned: &wrapperspb.BoolValue{Value: isPinned}})
-}
-
-func (c *Conversation) SetOneConversationPrivateChat(ctx context.Context, conversationID string, isPrivate bool) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsPrivateChat: &wrapperspb.BoolValue{Value: isPrivate}})
-}
-
-func (c *Conversation) SetConversationMsgDestructTime(ctx context.Context, conversationID string, msgDestructTime int64) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{MsgDestructTime: &wrapperspb.Int64Value{Value: msgDestructTime}})
-}
-
-func (c *Conversation) SetConversationIsMsgDestruct(ctx context.Context, conversationID string, isMsgDestruct bool) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsMsgDestruct: &wrapperspb.BoolValue{Value: isMsgDestruct}})
-}
-
-func (c *Conversation) SetOneConversationBurnDuration(ctx context.Context, conversationID string, burnDuration int32) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{BurnDuration: &wrapperspb.Int32Value{Value: burnDuration}})
-}
-
-func (c *Conversation) SetOneConversationRecvMessageOpt(ctx context.Context, conversationID string, opt int) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{RecvMsgOpt: &wrapperspb.Int32Value{Value: int32(opt)}})
-}
-func (c *Conversation) SetOneConversationEx(ctx context.Context, conversationID string, ex string) error {
- return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{Ex: &wrapperspb.StringValue{
- Value: ex,
- }})
-}
func (c *Conversation) GetTotalUnreadMsgCount(ctx context.Context) (totalUnreadCount int32, err error) {
return c.db.GetTotalUnreadMsgCountDB(ctx)
}
@@ -208,68 +148,6 @@ func (c *Conversation) SetConversationListener(listener func() open_im_sdk_callb
c.ConversationListener = listener
}
-func (c *Conversation) msgStructToLocalChatLog(src *sdk_struct.MsgStruct) *model_struct.LocalChatLog {
- var lc model_struct.LocalChatLog
- copier.Copy(&lc, src)
- switch src.ContentType {
- case constant.Text:
- lc.Content = utils.StructToJsonString(src.TextElem)
- case constant.Picture:
- lc.Content = utils.StructToJsonString(src.PictureElem)
- case constant.Sound:
- lc.Content = utils.StructToJsonString(src.SoundElem)
- case constant.Video:
- lc.Content = utils.StructToJsonString(src.VideoElem)
- case constant.File:
- lc.Content = utils.StructToJsonString(src.FileElem)
- case constant.AtText:
- lc.Content = utils.StructToJsonString(src.AtTextElem)
- case constant.Merger:
- lc.Content = utils.StructToJsonString(src.MergeElem)
- case constant.Card:
- lc.Content = utils.StructToJsonString(src.CardElem)
- case constant.Location:
- lc.Content = utils.StructToJsonString(src.LocationElem)
- case constant.Custom:
- lc.Content = utils.StructToJsonString(src.CustomElem)
- case constant.Quote:
- lc.Content = utils.StructToJsonString(src.QuoteElem)
- case constant.Face:
- lc.Content = utils.StructToJsonString(src.FaceElem)
- case constant.AdvancedText:
- lc.Content = utils.StructToJsonString(src.AdvancedTextElem)
- default:
- lc.Content = utils.StructToJsonString(src.NotificationElem)
- }
- if src.SessionType == constant.GroupChatType || src.SessionType == constant.SuperGroupChatType {
- lc.RecvID = src.GroupID
- }
- lc.AttachedInfo = utils.StructToJsonString(src.AttachedInfoElem)
- return &lc
-}
-func (c *Conversation) msgDataToLocalChatLog(src *sdkws.MsgData) *model_struct.LocalChatLog {
- var lc model_struct.LocalChatLog
- copier.Copy(&lc, src)
- lc.Content = string(src.Content)
- if src.SessionType == constant.GroupChatType || src.SessionType == constant.SuperGroupChatType {
- lc.RecvID = src.GroupID
-
- }
- return &lc
-
-}
-func (c *Conversation) msgDataToLocalErrChatLog(src *model_struct.LocalChatLog) *model_struct.LocalErrChatLog {
- var lc model_struct.LocalErrChatLog
- copier.Copy(&lc, src)
- return &lc
-
-}
-
-func localChatLogToMsgStruct(dst *sdk_struct.NewMsgList, src []*model_struct.LocalChatLog) {
- copier.Copy(dst, &src)
-
-}
-
func (c *Conversation) updateMsgStatusAndTriggerConversation(ctx context.Context, clientMsgID, serverMsgID string, sendTime int64, status int32, s *sdk_struct.MsgStruct,
lc *model_struct.LocalConversation, isOnlineOnly bool) {
log.ZDebug(ctx, "this is test send message ", "sendTime", sendTime, "status", status, "clientMsgID", clientMsgID, "serverMsgID", serverMsgID)
@@ -290,7 +168,6 @@ func (c *Conversation) updateMsgStatusAndTriggerConversation(ctx context.Context
}
lc.LatestMsg = utils.StructToJsonString(s)
lc.LatestMsgSendTime = sendTime
- // log.Info("", "2 send message come here", *lc)
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: lc.ConversationID, Action: constant.AddConOrUpLatMsg, Args: *lc}, c.GetCh())
}
@@ -306,9 +183,9 @@ func (c *Conversation) checkID(ctx context.Context, s *sdk_struct.MsgStruct,
s.SendID = c.loginUserID
s.SenderPlatformID = c.platformID
lc := &model_struct.LocalConversation{LatestMsgSendTime: s.CreateTime}
- //根据单聊群聊类型组装消息和会话
+ //assemble messages and conversations based on single or group chat types
if recvID == "" {
- g, err := c.full.GetGroupInfoByGroupID(ctx, groupID)
+ g, err := c.group.FetchGroupOrError(ctx, groupID)
if err != nil {
return nil, err
}
@@ -316,13 +193,13 @@ func (c *Conversation) checkID(ctx context.Context, s *sdk_struct.MsgStruct,
lc.FaceURL = g.FaceURL
switch g.GroupType {
case constant.NormalGroup:
- s.SessionType = constant.GroupChatType
- lc.ConversationType = constant.GroupChatType
- lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.GroupChatType)
+ s.SessionType = constant.WriteGroupChatType
+ lc.ConversationType = constant.WriteGroupChatType
+ lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.WriteGroupChatType)
case constant.SuperGroup, constant.WorkingGroup:
- s.SessionType = constant.SuperGroupChatType
- lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.SuperGroupChatType)
- lc.ConversationType = constant.SuperGroupChatType
+ s.SessionType = constant.ReadGroupChatType
+ lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.ReadGroupChatType)
+ lc.ConversationType = constant.ReadGroupChatType
}
s.GroupID = groupID
lc.GroupID = groupID
@@ -376,24 +253,29 @@ func (c *Conversation) getConversationIDBySessionType(sourceID string, sessionTy
l := []string{c.loginUserID, sourceID}
sort.Strings(l)
return "si_" + strings.Join(l, "_") // single chat
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "g_" + sourceID // group chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "sg_" + sourceID // super group chat
case constant.NotificationChatType:
return "sn_" + sourceID + "_" + c.loginUserID // server notification chat
}
return ""
}
+
func (c *Conversation) GetConversationIDBySessionType(_ context.Context, sourceID string, sessionType int) string {
return c.getConversationIDBySessionType(sourceID, sessionType)
}
-//this is a test file
-/**
-his is a test file
-*/
func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct, recvID, groupID string, p *sdkws.OfflinePushInfo, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
+ // Message is created by URL
+ if (s.FileElem != nil && s.FileElem.SourceURL != "") ||
+ (s.SoundElem != nil && s.SoundElem.SourceURL != "") ||
+ (s.VideoElem != nil && s.VideoElem.VideoURL != "") ||
+ (s.PictureElem != nil && (s.PictureElem.SourcePicture.Url != "" || s.PictureElem.BigPicture.Url != "" || s.PictureElem.SnapshotPicture.Url != "")) {
+ return c.sendMessageNotOss(ctx, s, recvID, groupID, p, isOnlineOnly)
+ }
+
filepathExt := func(name ...string) string {
for _, path := range name {
if ext := filepath.Ext(path); ext != "" {
@@ -412,7 +294,7 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
if !isOnlineOnly {
oldMessage, err := c.db.GetMessage(ctx, lc.ConversationID, s.ClientMsgID)
if err != nil {
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
err := c.db.InsertMessage(ctx, lc.ConversationID, localMessage)
if err != nil {
return nil, err
@@ -459,7 +341,6 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
sourcePath = utils.FileTmpPath(s.PictureElem.SourcePath, c.DataDir)
delFile = append(delFile, sourcePath)
}
- // log.Info("", "file", sourcePath, delFile)
log.ZDebug(ctx, "send picture", "path", sourcePath)
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
@@ -571,6 +452,7 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
if err != nil {
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime, constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
putErrs = err
+ return
}
if res != nil {
s.VideoElem.VideoURL = res.URL
@@ -587,15 +469,26 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
break
}
name := s.FileElem.FileName
+
if name == "" {
name = s.FileElem.FilePath
}
if name == "" {
name = fmt.Sprintf("msg_file_%s.unknown", s.ClientMsgID)
}
+
+ var sourcePath string
+ if utils.FileExist(s.FileElem.FilePath) {
+ sourcePath = s.FileElem.FilePath
+ delFile = append(delFile, utils.FileTmpPath(s.FileElem.FilePath, c.DataDir))
+ } else {
+ sourcePath = utils.FileTmpPath(s.FileElem.FilePath, c.DataDir)
+ delFile = append(delFile, sourcePath)
+ }
+
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
ContentType: content_type.GetType(s.FileElem.FileType, filepath.Ext(s.FileElem.FilePath), filepath.Ext(s.FileElem.FileName)),
- Filepath: s.FileElem.FilePath,
+ Filepath: sourcePath,
Uuid: s.FileElem.UUID,
Name: c.fileName("file", s.ClientMsgID) + "/" + filepath.Base(name),
Cause: "msg-file",
@@ -617,6 +510,11 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
case constant.Merger:
s.Content = utils.StructToJsonString(s.MergeElem)
case constant.Quote:
+ quoteMessage, err := c.db.GetMessage(ctx, lc.ConversationID, s.QuoteElem.QuoteMessage.ClientMsgID)
+ if err != nil {
+ log.ZWarn(ctx, "quote message not found", err)
+ }
+ s.QuoteElem.QuoteMessage.Seq = quoteMessage.Seq
s.Content = utils.StructToJsonString(s.QuoteElem)
case constant.Card:
s.Content = utils.StructToJsonString(s.CardElem)
@@ -624,12 +522,14 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
s.Content = utils.StructToJsonString(s.FaceElem)
case constant.AdvancedText:
s.Content = utils.StructToJsonString(s.AdvancedTextElem)
+ case pconstant.Stream:
+ s.Content = utils.StructToJsonString(s.StreamElem)
default:
return nil, sdkerrs.ErrMsgContentTypeNotSupport
}
if utils.IsContainInt(int(s.ContentType), []int{constant.Picture, constant.Sound, constant.Video, constant.File}) {
if !isOnlineOnly {
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
log.ZDebug(ctx, "update message is ", "localMessage", localMessage)
err = c.db.UpdateMessage(ctx, lc.ConversationID, localMessage)
if err != nil {
@@ -639,9 +539,9 @@ func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct,
}
return c.sendMessageToServer(ctx, s, lc, callback, delFile, p, options, isOnlineOnly)
-
}
-func (c *Conversation) SendMessageNotOss(ctx context.Context, s *sdk_struct.MsgStruct, recvID, groupID string,
+
+func (c *Conversation) sendMessageNotOss(ctx context.Context, s *sdk_struct.MsgStruct, recvID, groupID string,
p *sdkws.OfflinePushInfo, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
options := make(map[string]bool, 2)
lc, err := c.checkID(ctx, s, recvID, groupID, options)
@@ -652,7 +552,7 @@ func (c *Conversation) SendMessageNotOss(ctx context.Context, s *sdk_struct.MsgS
if !isOnlineOnly {
oldMessage, err := c.db.GetMessage(ctx, lc.ConversationID, s.ClientMsgID)
if err != nil {
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
err := c.db.InsertMessage(ctx, lc.ConversationID, localMessage)
if err != nil {
return nil, err
@@ -708,12 +608,14 @@ func (c *Conversation) SendMessageNotOss(ctx context.Context, s *sdk_struct.MsgS
s.Content = utils.StructToJsonString(s.FaceElem)
case constant.AdvancedText:
s.Content = utils.StructToJsonString(s.AdvancedTextElem)
+ case pconstant.Stream:
+ s.Content = utils.StructToJsonString(s.StreamElem)
default:
return nil, sdkerrs.ErrMsgContentTypeNotSupport
}
if utils.IsContainInt(int(s.ContentType), []int{constant.Picture, constant.Sound, constant.Video, constant.File}) {
if isOnlineOnly {
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
err = c.db.UpdateMessage(ctx, lc.ConversationID, localMessage)
if err != nil {
return nil, err
@@ -724,7 +626,7 @@ func (c *Conversation) SendMessageNotOss(ctx context.Context, s *sdk_struct.MsgS
}
func (c *Conversation) sendMessageToServer(ctx context.Context, s *sdk_struct.MsgStruct, lc *model_struct.LocalConversation, callback open_im_sdk_callback.SendMsgCallBack,
- delFile []string, offlinePushInfo *sdkws.OfflinePushInfo, options map[string]bool, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
+ delFiles []string, offlinePushInfo *sdkws.OfflinePushInfo, options map[string]bool, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
if isOnlineOnly {
utils.SetSwitchFromOptions(options, constant.IsHistory, false)
utils.SetSwitchFromOptions(options, constant.IsPersistent, false)
@@ -776,13 +678,14 @@ func (c *Conversation) sendMessageToServer(ctx context.Context, s *sdk_struct.Ms
s.ServerMsgID = sendMsgResp.ServerMsgID
go func() {
//remove media cache file
- for _, v := range delFile {
- err := os.Remove(v)
+ for _, file := range delFiles {
+ err := os.Remove(file)
if err != nil {
- // log.Error("", "remove failed,", err.Error(), v)
+ log.ZError(ctx, "delete temp File is failed", err, "filePath", file)
}
- // log.Debug("", "remove file: ", v)
+ // log.ZDebug(ctx, "remove temp file:", "file", file)
}
+
c.updateMsgStatusAndTriggerConversation(ctx, sendMsgResp.ClientMsgID, sendMsgResp.ServerMsgID, sendMsgResp.SendTime, constant.MsgStatusSendSuccess, s, lc, isOnlineOnly)
}()
return s, nil
@@ -812,39 +715,8 @@ func (c *Conversation) FindMessageList(ctx context.Context, req []*sdk_params_ca
if err == nil {
var tempMessageList []*sdk_struct.MsgStruct
for _, message := range messages {
- temp := sdk_struct.MsgStruct{}
- temp.ClientMsgID = message.ClientMsgID
- temp.ServerMsgID = message.ServerMsgID
- temp.CreateTime = message.CreateTime
- temp.SendTime = message.SendTime
- temp.SessionType = message.SessionType
- temp.SendID = message.SendID
- temp.RecvID = message.RecvID
- temp.MsgFrom = message.MsgFrom
- temp.ContentType = message.ContentType
- temp.SenderPlatformID = message.SenderPlatformID
- temp.SenderNickname = message.SenderNickname
- temp.SenderFaceURL = message.SenderFaceURL
- temp.Content = message.Content
- temp.Seq = message.Seq
- temp.IsRead = message.IsRead
- temp.Status = message.Status
- temp.AttachedInfo = message.AttachedInfo
- temp.Ex = message.Ex
- temp.LocalEx = message.LocalEx
- err := c.msgHandleByContentType(&temp)
- if err != nil {
- log.ZError(ctx, "msgHandleByContentType err", err, "message", temp)
- continue
- }
- switch message.SessionType {
- case constant.GroupChatType:
- fallthrough
- case constant.SuperGroupChatType:
- temp.GroupID = temp.RecvID
- temp.RecvID = c.loginUserID
- }
- tempMessageList = append(tempMessageList, &temp)
+ temp := LocalChatLogToMsgStruct(message)
+ tempMessageList = append(tempMessageList, temp)
}
findResultItem := sdk_params_callback.SearchByConversationResult{}
findResultItem.ConversationID = v.conversation.ConversationID
@@ -872,6 +744,7 @@ func (c *Conversation) GetAdvancedHistoryMessageList(ctx context.Context, req sd
s := make([]*sdk_struct.MsgStruct, 0)
result.MessageList = s
}
+ c.streamMsgReplace(ctx, req.ConversationID, result.MessageList)
return result, nil
}
@@ -884,6 +757,7 @@ func (c *Conversation) GetAdvancedHistoryMessageListReverse(ctx context.Context,
s := make([]*sdk_struct.MsgStruct, 0)
result.MessageList = s
}
+ c.streamMsgReplace(ctx, req.ConversationID, result.MessageList)
return result, nil
}
@@ -895,35 +769,28 @@ func (c *Conversation) TypingStatusUpdate(ctx context.Context, recvID, msgTip st
return c.typingStatusUpdate(ctx, recvID, msgTip)
}
-// funcation (c *Conversation) MarkMessageAsReadByConID(ctx context.Context, conversationID string, msgIDList []string) error {
-// if len(msgIDList) == 0 {
-// _ = c.setOneConversationUnread(ctx, conversationID, 0)
-// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.UnreadCountSetZero}, c.GetCh())
-// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.GetCh())
-// return nil
-// }
-// return nil
-
-// }
-
-// deprecated
-// funcation (c *Conversation) MarkGroupMessageHasRead(ctx context.Context, groupID string) {
-// conversationID := c.getConversationIDBySessionType(groupID, constant.GroupChatType)
-// _ = c.setOneConversationUnread(ctx, conversationID, 0)
-// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.UnreadCountSetZero}, c.GetCh())
-// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.GetCh())
-// }
-
-// read draw
func (c *Conversation) MarkConversationMessageAsRead(ctx context.Context, conversationID string) error {
return c.markConversationMessageAsRead(ctx, conversationID)
}
+func (c *Conversation) MarkAllConversationMessageAsRead(ctx context.Context) error {
+ conversationIDs, err := c.db.FindAllUnreadConversationConversationID(ctx)
+ if err != nil {
+ return err
+ }
+ for _, conversationID := range conversationIDs {
+ if err = c.markConversationMessageAsRead(ctx, conversationID); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// deprecated
func (c *Conversation) MarkMessagesAsReadByMsgID(ctx context.Context, conversationID string, clientMsgIDs []string) error {
return c.markMessagesAsReadByMsgID(ctx, conversationID, clientMsgIDs)
}
-// delete
func (c *Conversation) DeleteMessageFromLocalStorage(ctx context.Context, conversationID string, clientMsgID string) error {
return c.deleteMessageFromLocal(ctx, conversationID, clientMsgID)
}
@@ -932,8 +799,8 @@ func (c *Conversation) DeleteMessage(ctx context.Context, conversationID string,
return c.deleteMessage(ctx, conversationID, clientMsgID)
}
-func (c *Conversation) DeleteAllMsgFromLocalAndSvr(ctx context.Context) error {
- return c.deleteAllMsgFromLocalAndSvr(ctx)
+func (c *Conversation) DeleteAllMsgFromLocalAndServer(ctx context.Context) error {
+ return c.deleteAllMsgFromLocalAndServer(ctx)
}
func (c *Conversation) DeleteAllMessageFromLocalStorage(ctx context.Context) error {
@@ -941,14 +808,13 @@ func (c *Conversation) DeleteAllMessageFromLocalStorage(ctx context.Context) err
}
func (c *Conversation) ClearConversationAndDeleteAllMsg(ctx context.Context, conversationID string) error {
- return c.clearConversationFromLocalAndSvr(ctx, conversationID, c.db.ClearConversation)
+ return c.clearConversationFromLocalAndServer(ctx, conversationID, c.db.ClearConversation)
}
func (c *Conversation) DeleteConversationAndDeleteAllMsg(ctx context.Context, conversationID string) error {
- return c.clearConversationFromLocalAndSvr(ctx, conversationID, c.db.ResetConversation)
+ return c.clearConversationFromLocalAndServer(ctx, conversationID, c.db.ResetConversation)
}
-// insert
func (c *Conversation) InsertSingleMessageToLocalStorage(ctx context.Context, s *sdk_struct.MsgStruct, recvID, sendID string) (*sdk_struct.MsgStruct, error) {
if recvID == "" || sendID == "" {
return nil, sdkerrs.ErrArgs
@@ -986,7 +852,7 @@ func (c *Conversation) InsertSingleMessageToLocalStorage(ctx context.Context, s
s.SendTime = utils.GetCurrentTimestampByMill()
s.SessionType = constant.SingleChatType
s.Status = constant.MsgStatusSendSuccess
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
conversation.LatestMsg = utils.StructToJsonString(s)
conversation.ConversationType = constant.SingleChatType
conversation.LatestMsgSendTime = s.SendTime
@@ -1026,7 +892,7 @@ func (c *Conversation) InsertGroupMessageToLocalStorage(ctx context.Context, s *
s.SendTime = utils.GetCurrentTimestampByMill()
s.SessionType = conversation.ConversationType
s.Status = constant.MsgStatusSendSuccess
- localMessage := c.msgStructToLocalChatLog(s)
+ localMessage := MsgStructToLocalChatLog(s)
conversation.LatestMsg = utils.StructToJsonString(s)
conversation.LatestMsgSendTime = s.SendTime
conversation.FaceURL = s.SenderFaceURL
@@ -1074,67 +940,35 @@ func (c *Conversation) initBasicInfo(ctx context.Context, message *sdk_struct.Ms
message.IsRead = false
message.Status = constant.MsgStatusSending
message.SendID = c.loginUserID
- userInfo, err := c.db.GetLoginUser(ctx, c.loginUserID)
+ userInfo, err := c.user.GetUserInfoWithCache(ctx, c.loginUserID)
if err != nil {
return err
- } else {
- message.SenderFaceURL = userInfo.FaceURL
- message.SenderNickname = userInfo.Nickname
}
+ message.SenderFaceURL = userInfo.FaceURL
+ message.SenderNickname = userInfo.Nickname
ClientMsgID := utils.GetMsgID(message.SendID)
message.ClientMsgID = ClientMsgID
message.MsgFrom = msgFrom
message.ContentType = contentType
message.SenderPlatformID = c.platformID
- message.IsExternalExtensions = c.IsExternalExtensions
return nil
}
-//// 删除本地和服务器
-//// 删除本地的话不用改服务器的数据
-//// 删除服务器的话,需要把本地的消息状态改成删除
-//funcation (c *Conversation) DeleteConversationFromLocalAndSvr(ctx context.Context, conversationID string) error {
-// // Use conversationID to remove conversations and messages from the server first
-// err := c.clearConversationFromSvr(ctx, conversationID)
-// if err != nil {
-// return err
-// }
-// return c.deleteConversation(ctx, conversationID)
-//}
-
func (c *Conversation) getConversationTypeByGroupID(ctx context.Context, groupID string) (conversationID string, conversationType int32, err error) {
- g, err := c.full.GetGroupInfoByGroupID(ctx, groupID)
+ g, err := c.group.FetchGroupOrError(ctx, groupID)
if err != nil {
- return "", 0, utils.Wrap(err, "get group info error")
+ return "", 0, errs.WrapMsg(err, "get group info error")
}
switch g.GroupType {
case constant.NormalGroup:
- return c.getConversationIDBySessionType(groupID, constant.GroupChatType), constant.GroupChatType, nil
+ return c.getConversationIDBySessionType(groupID, constant.WriteGroupChatType), constant.WriteGroupChatType, nil
case constant.SuperGroup, constant.WorkingGroup:
- return c.getConversationIDBySessionType(groupID, constant.SuperGroupChatType), constant.SuperGroupChatType, nil
+ return c.getConversationIDBySessionType(groupID, constant.ReadGroupChatType), constant.ReadGroupChatType, nil
default:
return "", 0, sdkerrs.ErrGroupType
}
}
-func (c *Conversation) SetMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req []*server_api_params.KeyValue) ([]*server_api_params.ExtensionResult, error) {
- return c.setMessageReactionExtensions(ctx, s, req)
-
-}
-
-func (c *Conversation) AddMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, reactionExtensionList []*server_api_params.KeyValue) ([]*server_api_params.ExtensionResult, error) {
- return c.addMessageReactionExtensions(ctx, s, reactionExtensionList)
-
-}
-
-func (c *Conversation) DeleteMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, reactionExtensionKeyList []string) ([]*server_api_params.ExtensionResult, error) {
- return c.deleteMessageReactionExtensions(ctx, s, reactionExtensionKeyList)
-}
-
-func (c *Conversation) GetMessageListReactionExtensions(ctx context.Context, conversationID string, messageList []*sdk_struct.MsgStruct) ([]*server_api_params.SingleMessageExtensionResult, error) {
- return c.getMessageListReactionExtensions(ctx, conversationID, messageList)
-
-}
func (c *Conversation) SearchConversation(ctx context.Context, searchParam string) ([]*server_api_params.Conversation, error) {
// Check if search parameter is empty
if searchParam == "" {
@@ -1173,39 +1007,10 @@ func (c *Conversation) SearchConversation(ctx context.Context, searchParam strin
// Return the list of conversations
return apiConversations, nil
}
+func (c *Conversation) GetInputStates(ctx context.Context, conversationID string, userID string) ([]int32, error) {
+ return c.typing.GetInputStates(conversationID, userID), nil
+}
-/**
-**Get some reaction extensions in reactionExtensionKeyList of message list
- */
-//funcation (c *Conversation) GetMessageListSomeReactionExtensions(ctx context.Context, messageList, reactionExtensionKeyList, operationID string) {
-// var messagelist []*sdk_struct.MsgStruct
-// common.JsonUnmarshalAndArgsValidate(messageList, &messagelist, callback, operationID)
-// var list []string
-// common.JsonUnmarshalAndArgsValidate(reactionExtensionKeyList, &list, callback, operationID)
-// result := c.getMessageListSomeReactionExtensions(callback, messagelist, list, operationID)
-// callback.OnSuccess(utils.StructToJsonString(result))
-// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
-//}
-//funcation (c *Conversation) SetTypeKeyInfo(ctx context.Context, message, typeKey, ex string, isCanRepeat bool, operationID string) {
-// s := sdk_struct.MsgStruct{}
-// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
-// result := c.setTypeKeyInfo(callback, &s, typeKey, ex, isCanRepeat, operationID)
-// callback.OnSuccess(utils.StructToJsonString(result))
-// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
-//}
-//funcation (c *Conversation) GetTypeKeyListInfo(ctx context.Context, message, typeKeyList, operationID string) {
-// s := sdk_struct.MsgStruct{}
-// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
-// var list []string
-// common.JsonUnmarshalAndArgsValidate(typeKeyList, &list, callback, operationID)
-// result := c.getTypeKeyListInfo(callback, &s, list, operationID)
-// callback.OnSuccess(utils.StructToJsonString(result))
-// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
-//}
-//funcation (c *Conversation) GetAllTypeKeyInfo(ctx context.Context, message, operationID string) {
-// s := sdk_struct.MsgStruct{}
-// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
-// result := c.getAllTypeKeyInfo(callback, &s, operationID)
-// callback.OnSuccess(utils.StructToJsonString(result))
-// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
-//}
+func (c *Conversation) ChangeInputStates(ctx context.Context, conversationID string, focus bool) error {
+ return c.typing.ChangeInputStates(ctx, conversationID, focus)
+}
diff --git a/internal/conversation_msg/conversation.go b/internal/conversation_msg/conversation.go
index bbd4da643..f4bed3dd0 100644
--- a/internal/conversation_msg/conversation.go
+++ b/internal/conversation_msg/conversation.go
@@ -17,26 +17,24 @@ package conversation_msg
import (
"context"
"errors"
- _ "github.com/openimsdk/openim-sdk-core/v3/internal/common"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/jinzhu/copier"
+ "golang.org/x/sync/errgroup"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/cache"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/utils/datautil"
- "sort"
- "strings"
- "time"
-
- "github.com/jinzhu/copier"
"github.com/openimsdk/tools/log"
- "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
pbConversation "github.com/openimsdk/protocol/conversation"
@@ -48,165 +46,216 @@ func (c *Conversation) setConversation(ctx context.Context, apiReq *pbConversati
apiReq.Conversation.UserID = localConversation.UserID
apiReq.Conversation.GroupID = localConversation.GroupID
apiReq.UserIDs = []string{c.loginUserID}
- if err := util.ApiPost(ctx, constant.SetConversationsRouter, apiReq, nil); err != nil {
- return err
- }
- return nil
-}
-func (c *Conversation) getServerConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error) {
- resp, err := util.CallApi[pbConversation.GetAllConversationsResp](ctx, constant.GetAllConversationsRouter, pbConversation.GetAllConversationsReq{OwnerUserID: c.loginUserID})
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerConversationToLocal, resp.Conversations), nil
-}
-
-func (c *Conversation) getServerConversationsByIDs(ctx context.Context, conversations []string) ([]*model_struct.LocalConversation, error) {
- resp, err := util.CallApi[pbConversation.GetConversationsResp](ctx, constant.GetConversationsRouter, pbConversation.GetConversationsReq{OwnerUserID: c.loginUserID, ConversationIDs: conversations})
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerConversationToLocal, resp.Conversations), nil
-}
-
-func (c *Conversation) getServerHasReadAndMaxSeqs(ctx context.Context, conversationIDs ...string) (map[string]*msg.Seqs, error) {
- resp := &msg.GetConversationsHasReadAndMaxSeqResp{}
- req := msg.GetConversationsHasReadAndMaxSeqReq{UserID: c.loginUserID}
- req.ConversationIDs = conversationIDs
- err := util.ApiPost(ctx, constant.GetConversationsHasReadAndMaxSeqRouter, &req, resp)
- if err != nil {
- log.ZError(ctx, "getServerHasReadAndMaxSeqs err", err)
- return nil, err
- }
- return resp.Seqs, nil
+ return api.SetConversations.Execute(ctx, apiReq)
}
func (c *Conversation) getAdvancedHistoryMessageList(ctx context.Context, req sdk.GetAdvancedHistoryMessageListParams, isReverse bool) (*sdk.GetAdvancedHistoryMessageListCallback, error) {
t := time.Now()
var messageListCallback sdk.GetAdvancedHistoryMessageListCallback
var conversationID string
- var startTime int64
- var sessionType int
- var list []*model_struct.LocalChatLog
+ var startClientMsgID string
+ var startTime, startSeq int64
var err error
var messageList sdk_struct.NewMsgList
- var notStartTime bool
conversationID = req.ConversationID
- lc, err := c.db.GetConversation(ctx, conversationID)
- if err != nil {
- return nil, err
- }
- sessionType = int(lc.ConversationType)
- if req.StartClientMsgID == "" {
- notStartTime = true
- } else {
+ if len(req.StartClientMsgID) > 0 {
m, err := c.db.GetMessage(ctx, conversationID, req.StartClientMsgID)
if err != nil {
return nil, err
}
startTime = m.SendTime
- }
- log.ZDebug(ctx, "Assembly conversation parameters", "cost time", time.Since(t), "conversationID",
- conversationID, "startTime:", startTime, "count:", req.Count, "not start_time", notStartTime)
- t = time.Now()
- if notStartTime {
- list, err = c.db.GetMessageListNoTime(ctx, conversationID, req.Count, isReverse)
+ startClientMsgID = req.StartClientMsgID
+ startSeq = m.Seq
+ err = c.handleEndSeq(ctx, req, isReverse, m)
+ if err != nil {
+ return nil, err
+ }
} else {
- list, err = c.db.GetMessageList(ctx, conversationID, req.Count, startTime, isReverse)
+ // Clear both maps when the user enters the conversation
+ c.messagePullForwardEndSeqMap.Delete(conversationID, req.ViewType)
+ c.messagePullReverseEndSeqMap.Delete(conversationID, req.ViewType)
}
- log.ZDebug(ctx, "db get messageList", "cost time", time.Since(t), "len", len(list), "err",
- err, "conversationID", conversationID)
+ log.ZDebug(ctx, "Assembly conversation parameters", "cost time", time.Since(t), "conversationID",
+ conversationID, "startTime:", startTime, "count:", req.Count)
+ list, err := c.fetchMessagesWithGapCheck(ctx, conversationID, req.Count, startTime, startSeq, startClientMsgID, isReverse, req.ViewType, &messageListCallback)
if err != nil {
return nil, err
}
- rawMessageLength := len(list)
+ log.ZDebug(ctx, "pull message", "pull cost time", time.Since(t).Milliseconds())
t = time.Now()
- if rawMessageLength < req.Count {
- maxSeq, minSeq, lostSeqListLength := c.messageBlocksInternalContinuityCheck(ctx,
- conversationID, notStartTime, isReverse, req.Count, startTime, &list, &messageListCallback)
- _ = c.messageBlocksBetweenContinuityCheck(ctx, req.LastMinSeq, maxSeq, conversationID,
- notStartTime, isReverse, req.Count, startTime, &list, &messageListCallback)
- if minSeq == 1 && lostSeqListLength == 0 {
- messageListCallback.IsEnd = true
- } else {
- c.messageBlocksEndContinuityCheck(ctx, minSeq, conversationID, notStartTime, isReverse,
- req.Count, startTime, &list, &messageListCallback)
+
+ messageList = c.LocalChatLog2MsgStruct(list)
+ log.ZDebug(ctx, "message convert and unmarshal", "unmarshal cost time", time.Since(t))
+ t = time.Now()
+ if !isReverse {
+ sort.Sort(messageList)
+ }
+ log.ZDebug(ctx, "sort", "sort cost time", time.Since(t))
+ messageListCallback.MessageList = messageList
+
+ return &messageListCallback, nil
+}
+func (c *Conversation) handleEndSeq(ctx context.Context, req sdk.GetAdvancedHistoryMessageListParams, isReverse bool, startMessage *model_struct.LocalChatLog) error {
+ if isReverse {
+ if _, ok := c.messagePullReverseEndSeqMap.Load(req.ConversationID, req.ViewType); !ok {
+ if startMessage.Seq != 0 {
+ c.messagePullReverseEndSeqMap.Store(req.ConversationID, req.ViewType, startMessage.Seq)
+ } else {
+ validServerMessage, err := c.db.GetLatestValidServerMessage(ctx, req.ConversationID, startMessage.SendTime, isReverse)
+ if err != nil {
+ return err
+ }
+ if validServerMessage != nil {
+ c.messagePullReverseEndSeqMap.Store(req.ConversationID, req.ViewType, validServerMessage.Seq)
+ } else {
+ log.ZDebug(ctx, "no valid server message", "conversationID", req.ConversationID, "startTime", startMessage.SendTime)
+ }
+ }
}
+
} else {
- maxSeq, _, _ := c.messageBlocksInternalContinuityCheck(ctx, conversationID, notStartTime, isReverse,
- req.Count, startTime, &list, &messageListCallback)
- c.messageBlocksBetweenContinuityCheck(ctx, req.LastMinSeq, maxSeq, conversationID, notStartTime,
- isReverse, req.Count, startTime, &list, &messageListCallback)
+ if _, ok := c.messagePullForwardEndSeqMap.Load(req.ConversationID, req.ViewType); !ok {
+ if startMessage.Seq != 0 {
+ c.messagePullForwardEndSeqMap.Store(req.ConversationID, req.ViewType, startMessage.Seq)
+ } else {
+ validServerMessage, err := c.db.GetLatestValidServerMessage(ctx, req.ConversationID, startMessage.SendTime, isReverse)
+ if err != nil {
+ return err
+ }
+ if validServerMessage != nil {
+ c.messagePullForwardEndSeqMap.Store(req.ConversationID, req.ViewType, validServerMessage.Seq)
+ } else {
+ log.ZDebug(ctx, "no valid server message", "conversationID", req.ConversationID, "startTime", startMessage.SendTime)
+ }
+ }
+ }
}
- log.ZDebug(ctx, "pull message", "pull cost time", time.Since(t))
- t = time.Now()
- var thisMinSeq int64
- for _, v := range list {
- if v.Seq != 0 && thisMinSeq == 0 {
- thisMinSeq = v.Seq
+ return nil
+}
+
+func (c *Conversation) fetchMessagesWithGapCheck(ctx context.Context, conversationID string,
+ count int, startTime, startSeq int64, startClientMsgID string, isReverse bool, viewType int, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) ([]*model_struct.LocalChatLog, error) {
+
+ var list, validMessages []*model_struct.LocalChatLog
+
+ // Get the number of invalid messages in this batch to recursive fetching from earlier points.
+ shouldFetchMoreMessagesNum := func(messages []*model_struct.LocalChatLog) int {
+ var thisEndSeq int64
+ // Represents the number of valid messages in the batch
+ validateMessageNum := 0
+ for _, msg := range messages {
+ if msg.Seq != 0 && thisEndSeq == 0 {
+ thisEndSeq = msg.Seq
+ }
+ if isReverse {
+ if msg.Seq > thisEndSeq && thisEndSeq != 0 {
+ thisEndSeq = msg.Seq
+ }
+
+ } else {
+ if msg.Seq < thisEndSeq && msg.Seq != 0 {
+ thisEndSeq = msg.Seq
+ }
+ }
+ if msg.Status >= constant.MsgStatusHasDeleted {
+ log.ZDebug(ctx, "this message has been deleted or exception message", "msg", msg)
+ continue
+ }
+
+ validateMessageNum++
+ validMessages = append(validMessages, msg)
+
}
- if v.Seq < thisMinSeq && v.Seq != 0 {
- thisMinSeq = v.Seq
+ if !isReverse {
+ if thisEndSeq != 0 {
+ c.messagePullForwardEndSeqMap.StoreWithFunc(conversationID, viewType, thisEndSeq, func(_ string, value int64) bool {
+ lastEndSeq, _ := c.messagePullForwardEndSeqMap.Load(conversationID, viewType)
+ if value < lastEndSeq || lastEndSeq == 0 {
+ log.ZDebug(ctx, "update the end sequence of the message", "lastEndSeq", lastEndSeq, "thisEndSeq", value)
+ return true
+ }
+ log.ZWarn(ctx, "The end sequence number of the message is more than the last end sequence number",
+ nil, "conversationID", conversationID, "value", value, "lastEndSeq", lastEndSeq)
+ return false
+ })
+ }
+ } else {
+ if thisEndSeq != 0 {
+ c.messagePullReverseEndSeqMap.StoreWithFunc(conversationID, viewType, thisEndSeq, func(_ string, value int64) bool {
+ lastEndSeq, _ := c.messagePullReverseEndSeqMap.Load(conversationID, viewType)
+ if value > lastEndSeq || lastEndSeq == 0 {
+ log.ZDebug(ctx, "update the end sequence of the message", "lastEndSeq", lastEndSeq, "thisEndSeq", value)
+ return true
+ }
+ log.ZWarn(ctx, "The end sequence number of the message is less than the last end sequence number",
+ nil, "conversationID", conversationID, "value", value, "lastEndSeq", lastEndSeq)
+ return false
+ })
+ }
}
- if v.Status >= constant.MsgStatusHasDeleted {
- log.ZDebug(ctx, "this message has been deleted or exception message", "msg", v)
- continue
+
+ return count - validateMessageNum
+ }
+ getNewStartMessageInfo := func(messages []*model_struct.LocalChatLog) (int64, int64, string) {
+ if len(messages) == 0 {
+ return 0, 0, ""
}
- temp := sdk_struct.MsgStruct{}
- temp.ClientMsgID = v.ClientMsgID
- temp.ServerMsgID = v.ServerMsgID
- temp.CreateTime = v.CreateTime
- temp.SendTime = v.SendTime
- temp.SessionType = v.SessionType
- temp.SendID = v.SendID
- temp.RecvID = v.RecvID
- temp.MsgFrom = v.MsgFrom
- temp.ContentType = v.ContentType
- temp.SenderPlatformID = v.SenderPlatformID
- temp.SenderNickname = v.SenderNickname
- temp.SenderFaceURL = v.SenderFaceURL
- temp.Content = v.Content
- temp.Seq = v.Seq
- temp.IsRead = v.IsRead
- temp.Status = v.Status
- var attachedInfo sdk_struct.AttachedInfoElem
- _ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
- temp.AttachedInfoElem = &attachedInfo
- temp.Ex = v.Ex
- temp.LocalEx = v.LocalEx
- err := c.msgHandleByContentType(&temp)
+ // Returns the SendTime and ClientMsgID of the last element in the message list
+ return messages[len(messages)-1].SendTime, messages[len(messages)-1].Seq, messages[len(messages)-1].ClientMsgID
+ }
+
+ t := time.Now()
+ list, err := c.db.GetMessageList(ctx, conversationID, count, startTime, startSeq, startClientMsgID, isReverse)
+ log.ZDebug(ctx, "db get messageList", "cost time", time.Since(t), "len", len(list), "err",
+ err, "conversationID", conversationID)
+
+ if err != nil {
+ return nil, err
+ }
+ t = time.Now()
+ thisStartSeq := c.validateAndFillInternalGaps(ctx, conversationID, isReverse,
+ count, startTime, &list, messageListCallback)
+ log.ZDebug(ctx, "internal continuity check over", "cost time", time.Since(t), "thisStartSeq", thisStartSeq)
+ t = time.Now()
+ c.validateAndFillInterBlockGaps(ctx, thisStartSeq, conversationID,
+ isReverse, viewType, count, startTime, &list, messageListCallback)
+ log.ZDebug(ctx, "between continuity check over", "cost time", time.Since(t), "thisStartSeq", thisStartSeq)
+ t = time.Now()
+ c.validateAndFillEndBlockContinuity(ctx, conversationID, isReverse, viewType,
+ count, startTime, &list, messageListCallback)
+ log.ZDebug(ctx, "end continuity check over", "cost time", time.Since(t))
+ // If the number of valid messages retrieved is less than the count,
+ // continue fetching recursively until the valid messages are sufficient or all messages have been fetched.
+ missingCount := shouldFetchMoreMessagesNum(list)
+ if missingCount > 0 && !messageListCallback.IsEnd {
+ newStartTime, newStartSeq, newStartClientMsgID := getNewStartMessageInfo(list)
+ log.ZDebug(ctx, "fetch more messages", "missingCount", missingCount, "conversationID",
+ conversationID, "newStartTime", newStartTime, "newStartSeq", newStartSeq, "newStartClientMsgID", newStartClientMsgID)
+ missingMessages, err := c.fetchMessagesWithGapCheck(ctx, conversationID, missingCount, newStartTime, newStartSeq, newStartClientMsgID, isReverse, viewType, messageListCallback)
if err != nil {
- log.ZError(ctx, "Parsing data error", err, "temp", temp)
- continue
- }
- switch sessionType {
- case constant.GroupChatType:
- fallthrough
- case constant.SuperGroupChatType:
- temp.GroupID = temp.RecvID
- temp.RecvID = c.loginUserID
+ return nil, err
}
- if attachedInfo.IsPrivateChat && temp.SendTime+int64(attachedInfo.BurnDuration) < time.Now().Unix() {
+ log.ZDebug(ctx, "fetch more messages", "missingMessages", missingMessages)
+ return append(validMessages, missingMessages...), nil
+ }
+
+ return validMessages, nil
+}
+
+func (c *Conversation) LocalChatLog2MsgStruct(list []*model_struct.LocalChatLog) []*sdk_struct.MsgStruct {
+ messageList := make([]*sdk_struct.MsgStruct, 0, len(list))
+ for _, v := range list {
+ temp := LocalChatLogToMsgStruct(v)
+
+ if temp.AttachedInfoElem.IsPrivateChat && temp.SendTime+int64(temp.AttachedInfoElem.BurnDuration) < time.Now().Unix() {
continue
}
- messageList = append(messageList, &temp)
- }
- log.ZDebug(ctx, "message convert and unmarshal", "unmarshal cost time", time.Since(t))
- t = time.Now()
- if !isReverse {
- sort.Sort(messageList)
+ messageList = append(messageList, temp)
}
- log.ZDebug(ctx, "sort", "sort cost time", time.Since(t))
- messageListCallback.MessageList = messageList
- if thisMinSeq == 0 {
- thisMinSeq = req.LastMinSeq
- }
- messageListCallback.LastMinSeq = thisMinSeq
- return &messageListCallback, nil
-
+ return messageList
}
func (c *Conversation) typingStatusUpdate(ctx context.Context, recvID, msgTip string) error {
@@ -247,46 +296,6 @@ func (c *Conversation) typingStatusUpdate(ctx context.Context, recvID, msgTip st
}
-// funcation (c *Conversation) markMessageAsReadByConID(callback open_im_sdk_callback.Base, msgIDList sdk.MarkMessageAsReadByConIDParams, conversationID, operationID string) {
-// var localMessage db.LocalChatLog
-// var newMessageIDList []string
-// messages, err := c.db.GetMultipleMessage(msgIDList)
-// common.CheckDBErrCallback(callback, err, operationID)
-// for _, v := range messages {
-// if v.IsRead == false && v.ContentType < constant.NotificationBegin && v.SendID != c.loginUserID {
-// newMessageIDList = append(newMessageIDList, v.ClientMsgID)
-// }
-// }
-// if len(newMessageIDList) == 0 {
-// common.CheckAnyErrCallback(callback, 201, errors.New("message has been marked read or sender is yourself"), operationID)
-// }
-// conversationID := c.getConversationIDBySessionType(userID, constant.SingleChatType)
-// s := sdk_struct.MsgStruct{}
-// c.initBasicInfo(&s, constant.UserMsgType, constant.HasReadReceipt, operationID)
-// s.Content = utils.StructToJsonString(newMessageIDList)
-// options := make(map[string]bool, 5)
-// utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false)
-// utils.SetSwitchFromOptions(options, constant.IsSenderConversationUpdate, false)
-// utils.SetSwitchFromOptions(options, constant.IsUnreadCount, false)
-// utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
-// //If there is an error, the coroutine ends, so judgment is not required
-// resp, _ := c.InternalSendMessage(callback, &s, userID, "", operationID, &server_api_params.OfflinePushInfo{}, false, options)
-// s.ServerMsgID = resp.ServerMsgID
-// s.SendTime = resp.SendTime
-// s.Status = constant.MsgStatusFiltered
-// msgStructToLocalChatLog(&localMessage, &s)
-// err = c.db.InsertMessage(&localMessage)
-// if err != nil {
-// log.Error(operationID, "inset into chat log err", localMessage, s, err.Error())
-// }
-// err2 := c.db.UpdateMessageHasRead(userID, newMessageIDList, constant.SingleChatType)
-// if err2 != nil {
-// log.Error(operationID, "update message has read error", newMessageIDList, userID, err2.Error())
-// }
-// _ = common.TriggerCmdUpdateConversation(common.UpdateConNode{ConID: conversationID, Action: constant.UpdateLatestMessageChange}, c.ch)
-// //_ = common.TriggerCmdUpdateConversation(common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.ch)
-// }
-
func (c *Conversation) insertMessageToLocalStorage(ctx context.Context, conversationID string, s *model_struct.LocalChatLog) error {
return c.db.InsertMessage(ctx, conversationID, s)
}
@@ -321,9 +330,14 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
var err error // Variable to store any errors encountered
var conversationID string // Variable to store the current conversation ID
+ // Clear the sequence cache for pull-up and pull-down operations in the search view,
+ // to prevent the completion operations from the previous round from affecting the next round
+ c.messagePullForwardEndSeqMap.DeleteByViewType(cache.ViewSearch)
+ c.messagePullReverseEndSeqMap.DeleteByViewType(cache.ViewSearch)
+
// Set the end time for the search; if SearchTimePosition is 0, use the current timestamp
if searchParam.SearchTimePosition == 0 {
- endTime = utils.GetCurrentTimestampBySecond()
+ endTime = time.Now().Unix()
} else {
endTime = searchParam.SearchTimePosition
}
@@ -352,9 +366,13 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
if err != nil {
return nil, err
}
+
// Search by content type or keyword based on provided parameters
if len(searchParam.MessageTypeList) != 0 && len(searchParam.KeywordList) == 0 {
- list, err = c.db.SearchMessageByContentType(ctx, searchParam.MessageTypeList, searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
+ list, err = c.db.SearchMessageByContentType(ctx, searchParam.MessageTypeList, searchParam.SenderUserIDList, searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
+ if err != nil {
+ return nil, err
+ }
} else {
newContentTypeList := func(list []int) (result []int) {
for _, v := range list {
@@ -364,18 +382,24 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
}
return result
}(searchParam.MessageTypeList)
+
if len(newContentTypeList) == 0 {
newContentTypeList = SearchContentType
}
- list, err = c.db.SearchMessageByKeyword(ctx, newContentTypeList, searchParam.KeywordList, searchParam.KeywordListMatchType,
- searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
+
+ list, err = c.db.SearchMessageByKeyword(ctx, newContentTypeList, searchParam.SenderUserIDList, searchParam.KeywordList,
+ searchParam.KeywordListMatchType, searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
+ if err != nil {
+ return nil, err
+ }
}
} else {
// Comprehensive search across all conversations
if len(searchParam.MessageTypeList) == 0 {
searchParam.MessageTypeList = SearchContentType
}
- list, err = c.messageController.SearchMessageByContentTypeAndKeyword(ctx, searchParam.MessageTypeList, searchParam.KeywordList, searchParam.KeywordListMatchType, startTime, endTime)
+
+ list, err = c.searchMessageByContentTypeAndKeyword(ctx, searchParam.MessageTypeList, searchParam.SenderUserIDList, searchParam.KeywordList, searchParam.KeywordListMatchType, startTime, endTime)
}
// Handle any errors encountered during the search
@@ -392,38 +416,11 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
//log.Debug("hahh",utils.KMP("SSSsdf3434","F3434"))
//log.Debug("hahh",utils.KMP("SSSsdf3434","SDF3"))
// log.Debug("", "get raw data length is", len(list))
- log.ZDebug(ctx, "get raw data length is", len(list))
+ log.ZDebug(ctx, "get raw data length is", "len", len(list))
for _, v := range list {
- temp := sdk_struct.MsgStruct{}
- temp.ClientMsgID = v.ClientMsgID
- temp.ServerMsgID = v.ServerMsgID
- temp.CreateTime = v.CreateTime
- temp.SendTime = v.SendTime
- temp.SessionType = v.SessionType
- temp.SendID = v.SendID
- temp.RecvID = v.RecvID
- temp.MsgFrom = v.MsgFrom
- temp.ContentType = v.ContentType
- temp.SenderPlatformID = v.SenderPlatformID
- temp.SenderNickname = v.SenderNickname
- temp.SenderFaceURL = v.SenderFaceURL
- temp.Content = v.Content
- temp.Seq = v.Seq
- temp.IsRead = v.IsRead
- temp.Status = v.Status
- var attachedInfo sdk_struct.AttachedInfoElem
- _ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
- temp.AttachedInfoElem = &attachedInfo
- temp.Ex = v.Ex
- temp.LocalEx = v.LocalEx
- err := c.msgHandleByContentType(&temp)
- if err != nil {
- // log.Error("", "Parsing data error:", err.Error(), temp)
- log.ZError(ctx, "Parsing data error:", err, "msg", temp)
- continue
- }
- if c.filterMsg(&temp, searchParam) {
+ temp := LocalChatLogToMsgStruct(v)
+ if c.filterMsg(temp, searchParam) {
continue
}
// Determine the conversation ID based on the session type
@@ -434,14 +431,10 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
} else {
conversationID = c.getConversationIDBySessionType(temp.SendID, constant.SingleChatType)
}
- case constant.GroupChatType:
- temp.GroupID = temp.RecvID
- temp.RecvID = c.loginUserID
- conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.GroupChatType)
- case constant.SuperGroupChatType:
- temp.GroupID = temp.RecvID
- temp.RecvID = c.loginUserID
- conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.SuperGroupChatType)
+ case constant.WriteGroupChatType:
+ conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.WriteGroupChatType)
+ case constant.ReadGroupChatType:
+ conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.ReadGroupChatType)
}
// Populate the conversationMap with search results
if oldItem, ok := conversationMap[conversationID]; !ok {
@@ -456,12 +449,12 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
searchResultItem.ShowName = localConversation.ShowName
searchResultItem.LatestMsgSendTime = localConversation.LatestMsgSendTime
searchResultItem.ConversationType = localConversation.ConversationType
- searchResultItem.MessageList = append(searchResultItem.MessageList, &temp)
+ searchResultItem.MessageList = append(searchResultItem.MessageList, temp)
searchResultItem.MessageCount++
conversationMap[conversationID] = &searchResultItem
} else {
oldItem.MessageCount++
- oldItem.MessageList = append(oldItem.MessageList, &temp)
+ oldItem.MessageList = append(oldItem.MessageList, temp)
conversationMap[conversationID] = oldItem
}
}
@@ -480,6 +473,43 @@ func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk
return &r, nil // Return the final search results
}
+func (c *Conversation) searchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, senderUserIDList []string, keywordList []string,
+ keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
+ var list []*model_struct.LocalChatLog
+
+ conversationIDList, err := c.db.GetAllConversationIDList(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ var mu sync.Mutex
+ eg, _ := errgroup.WithContext(ctx)
+ eg.SetLimit(searchMessageGoroutineLimit)
+ for _, cID := range conversationIDList {
+ conversationID := cID
+
+ eg.Go(func() error {
+ sList, err := c.db.SearchMessageByContentTypeAndKeyword(ctx, contentType, conversationID, senderUserIDList, keywordList, keywordListMatchType, startTime, endTime)
+ if err != nil {
+ log.ZWarn(ctx, "search conversation message", err, "conversationID", conversationID)
+ return nil
+ }
+
+ mu.Lock()
+ list = append(list, sList...)
+ mu.Unlock()
+
+ return nil
+ })
+ }
+
+ if err := eg.Wait(); err != nil {
+ return nil, err
+ }
+
+ return list, nil
+}
+
// true is filter, false is not filter
func (c *Conversation) filterMsg(temp *sdk_struct.MsgStruct, searchParam *sdk.SearchLocalMessagesParams) bool {
switch temp.ContentType {
@@ -517,6 +547,8 @@ func (c *Conversation) filterMsg(temp *sdk_struct.MsgStruct, searchParam *sdk.Se
}
case constant.Picture:
fallthrough
+ case constant.Sound:
+ fallthrough
case constant.Video:
if len(searchParam.KeywordList) == 0 {
return false
@@ -528,651 +560,3 @@ func (c *Conversation) filterMsg(temp *sdk_struct.MsgStruct, searchParam *sdk.Se
}
return false
}
-
-func (c *Conversation) delMsgBySeq(seqList []uint32) error {
- var SPLIT = 1000
- for i := 0; i < len(seqList)/SPLIT; i++ {
- if err := c.delMsgBySeqSplit(seqList[i*SPLIT : (i+1)*SPLIT]); err != nil {
- return utils.Wrap(err, "")
- }
- }
- return nil
-}
-
-func (c *Conversation) delMsgBySeqSplit(seqList []uint32) error {
- // var req server_api_params.DelMsgListReq
- // req.SeqList = seqList
- // req.OperationID = utils.OperationIDGenerator()
- // req.OpUserID = c.loginUserID
- // req.UserID = c.loginUserID
- // operationID := req.OperationID
-
- // err := c.SendReqWaitResp(context.Background(), &req, constant.WsDelMsg, 30, c.loginUserID)
- // if err != nil {
- // return utils.Wrap(err, "SendReqWaitResp failed")
- // }
- // var delResp server_api_params.DelMsgListResp
- // err = proto.Unmarshal(resp.Data, &delResp)
- // if err != nil {
- // log.Error(operationID, "Unmarshal failed ", err.Error())
- // return utils.Wrap(err, "Unmarshal failed")
- // }
- return nil
-}
-
-// old WS method
-//funcation (c *Conversation) deleteMessageFromSvr(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, operationID string) {
-// seq, err := c.db.GetMsgSeqByClientMsgID(s.ClientMsgID)
-// common.CheckDBErrCallback(callback, err, operationID)
-// if seq == 0 {
-// err = errors.New("seq == 0 ")
-// common.CheckArgsErrCallback(callback, err, operationID)
-// }
-// seqList := []uint32{seq}
-// err = c.delMsgBySeq(seqList)
-// common.CheckArgsErrCallback(callback, err, operationID)
-//}
-
-func isContainMessageReaction(reactionType int, list []*sdk_struct.ReactionElem) (bool, *sdk_struct.ReactionElem) {
- for _, v := range list {
- if v.Type == reactionType {
- return true, v
- }
- }
- return false, nil
-}
-
-func isContainUserReactionElem(useID string, list []*sdk_struct.UserReactionElem) (bool, *sdk_struct.UserReactionElem) {
- for _, v := range list {
- if v.UserID == useID {
- return true, v
- }
- }
- return false, nil
-}
-
-func DeleteUserReactionElem(a []*sdk_struct.UserReactionElem, userID string) []*sdk_struct.UserReactionElem {
- j := 0
- for _, v := range a {
- if v.UserID != userID {
- a[j] = v
- j++
- }
- }
- return a[:j]
-}
-
-func (c *Conversation) setMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.SetMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
- return nil, nil
- //message, err := c.db.GetMessageController(ctx, s)
- //if err != nil {
- // return nil, err
- //}
- //if message.Status != constant.MsgStatusSendSuccess {
- // return nil, errors.New("only send success message can modify reaction extensions")
- //}
- //if message.SessionType != constant.SuperGroupChatType {
- // return nil, errors.New("currently only support super group message")
- //
- //}
- //extendMsg, _ := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
- //temp := make(map[string]*server_api_params.KeyValue)
- //_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
- //reqTemp := make(map[string]*server_api_params.KeyValue)
- //for _, v := range req {
- // if value, ok := temp[v.TypeKey]; ok {
- // v.LatestUpdateTime = value.LatestUpdateTime
- // }
- // reqTemp[v.TypeKey] = v
- //}
- //var sourceID string
- //switch message.SessionType {
- //case constant.SingleChatType:
- // if message.SendID == c.loginUserID {
- // sourceID = message.RecvID
- // } else {
- // sourceID = message.SendID
- // }
- //case constant.NotificationChatType:
- // sourceID = message.RecvID
- //case constant.GroupChatType, constant.SuperGroupChatType:
- // sourceID = message.RecvID
- //}
- //var apiReq server_api_params.SetMessageReactionExtensionsReq
- //apiReq.IsReact = message.IsReact
- //apiReq.ClientMsgID = message.ClientMsgID
- //apiReq.SourceID = sourceID
- //apiReq.SessionType = message.SessionType
- //apiReq.IsExternalExtensions = message.IsExternalExtensions
- //apiReq.ReactionExtensionList = reqTemp
- //apiReq.OperationID = ""
- //apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
- //resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.SetMessageReactionExtensionsRouter, &apiReq)
- //if err != nil {
- // return nil, err
- //}
- //var msg model_struct.LocalChatLogReactionExtensions
- //msg.ClientMsgID = message.ClientMsgID
- //resultKeyMap := make(map[string]*sdkws.KeyValue)
- //for _, v := range resp.Result {
- // if v.ErrCode == 0 {
- // temp := new(sdkws.KeyValue)
- // temp.TypeKey = v.TypeKey
- // temp.Value = v.Value
- // temp.LatestUpdateTime = v.LatestUpdateTime
- // resultKeyMap[v.TypeKey] = temp
- // }
- //}
- //err = c.db.GetAndUpdateMessageReactionExtension(ctx, message.ClientMsgID, resultKeyMap)
- //if err != nil {
- // log.Error("", "GetAndUpdateMessageReactionExtension err:", err.Error())
- //}
- //if !message.IsReact {
- // message.IsReact = resp.IsReact
- // message.MsgFirstModifyTime = resp.MsgFirstModifyTime
- // err = c.db.UpdateMessageController(ctx, message)
- // if err != nil {
- // log.Error("", "UpdateMessageController err:", err.Error(), message)
- //
- // }
- //}
- //return resp.Result, nil
-}
-
-func (c *Conversation) addMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.AddMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
- return nil, nil
- //message, err := c.db.GetMessageController(ctx, s)
- //if err != nil {
- // return nil, err
- //}
- //if message.Status != constant.MsgStatusSendSuccess || message.Seq == 0 {
- // return nil, errors.New("only send success message can modify reaction extensions")
- //}
- //reqTemp := make(map[string]*server_api_params.KeyValue)
- //extendMsg, err := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
- //if err == nil && extendMsg != nil {
- // temp := make(map[string]*server_api_params.KeyValue)
- // _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
- // for _, v := range req {
- // if value, ok := temp[v.TypeKey]; ok {
- // v.LatestUpdateTime = value.LatestUpdateTime
- // }
- // reqTemp[v.TypeKey] = v
- // }
- //} else {
- // for _, v := range req {
- // reqTemp[v.TypeKey] = v
- // }
- //}
- //var sourceID string
- //switch message.SessionType {
- //case constant.SingleChatType:
- // if message.SendID == c.loginUserID {
- // sourceID = message.RecvID
- // } else {
- // sourceID = message.SendID
- // }
- //case constant.NotificationChatType:
- // sourceID = message.RecvID
- //case constant.GroupChatType, constant.SuperGroupChatType:
- // sourceID = message.RecvID
- //}
- //var apiReq server_api_params.AddMessageReactionExtensionsReq
- //apiReq.IsReact = message.IsReact
- //apiReq.ClientMsgID = message.ClientMsgID
- //apiReq.SourceID = sourceID
- //apiReq.SessionType = message.SessionType
- //apiReq.IsExternalExtensions = message.IsExternalExtensions
- //apiReq.ReactionExtensionList = reqTemp
- //apiReq.OperationID = ""
- //apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
- //apiReq.Seq = message.Seq
- //
- //resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.AddMessageReactionExtensionsRouter, &apiReq)
- //if err != nil {
- // return nil, err
- //}
- //log.Debug("", "api return:", message.IsReact, resp)
- //if !message.IsReact {
- // message.IsReact = resp.IsReact
- // message.MsgFirstModifyTime = resp.MsgFirstModifyTime
- // err = c.db.UpdateMessageController(ctx, message)
- // if err != nil {
- // log.Error("", "UpdateMessageController err:", err.Error(), message)
- // }
- //}
- //return resp.Result, nil
-}
-
-func (c *Conversation) deleteMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.DeleteMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
- // message, err := c.GetMessageController(ctx, s)
- // if err != nil {
- // return nil, err
- // }
- // if message.Status != constant.MsgStatusSendSuccess {
- // return nil, errors.New("only send success message can modify reaction extensions")
- // }
- // if message.SessionType != constant.SuperGroupChatType {
- // return nil, errors.New("currently only support super group message")
-
- // }
- // extendMsg, _ := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
- // temp := make(map[string]*server_api_params.KeyValue)
- // _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
- // var reqTemp []*server_api_params.KeyValue
- // for _, v := range req {
- // if value, ok := temp[v]; ok {
- // var tt server_api_params.KeyValue
- // tt.LatestUpdateTime = value.LatestUpdateTime
- // tt.TypeKey = v
- // reqTemp = append(reqTemp, &tt)
- // }
- // }
- // var sourceID string
- // switch message.SessionType {
- // case constant.SingleChatType:
- // if message.SendID == c.loginUserID {
- // sourceID = message.RecvID
- // } else {
- // sourceID = message.SendID
- // }
- // case constant.NotificationChatType:
- // sourceID = message.RecvID
- // case constant.GroupChatType, constant.SuperGroupChatType:
- // sourceID = message.RecvID
- // }
- // var apiReq server_api_params.DeleteMessageReactionExtensionsReq
- // apiReq.ClientMsgID = message.ClientMsgID
- // apiReq.SourceID = sourceID
- // apiReq.SessionType = message.SessionType
- // apiReq.ReactionExtensionList = reqTemp
- // apiReq.OperationID = ""
- // apiReq.IsExternalExtensions = message.IsExternalExtensions
- // apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
- // resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.AddMessageReactionExtensionsRouter, &apiReq)
- // if err != nil {
- // return nil, err
- // }
- // var msg model_struct.LocalChatLogReactionExtensions
- // msg.ClientMsgID = message.ClientMsgID
- // resultKeyMap := make(map[string]*sdkws.KeyValue)
- // for _, v := range resp.Result {
- // if v.ErrCode == 0 {
- // temp := new(sdkws.KeyValue)
- // temp.TypeKey = v.TypeKey
- // resultKeyMap[v.TypeKey] = temp
- // }
- // }
- // err = c.db.DeleteAndUpdateMessageReactionExtension(ctx, message.ClientMsgID, resultKeyMap)
- // if err != nil {
- // log.Error("", "GetAndUpdateMessageReactionExtension err:", err.Error())
- // }
- // return resp.Result, nil
- return nil, nil
-}
-
-type syncReactionExtensionParams struct {
- MessageList []*model_struct.LocalChatLog
- SessionType int32
- SourceID string
- IsExternalExtension bool
- ExtendMessageList []*model_struct.LocalChatLogReactionExtensions
- TypeKeyList []string
-}
-
-func (c *Conversation) getMessageListReactionExtensions(ctx context.Context, conversationID string, messageList []*sdk_struct.MsgStruct) ([]*server_api_params.SingleMessageExtensionResult, error) {
- if len(messageList) == 0 {
- return nil, errors.New("message list is null")
- }
- var msgIDs []string
- var sourceID string
- var sessionType int32
- var isExternalExtension bool
- for _, msgStruct := range messageList {
- switch msgStruct.SessionType {
- case constant.SingleChatType:
- if msgStruct.SendID == c.loginUserID {
- sourceID = msgStruct.RecvID
- } else {
- sourceID = msgStruct.SendID
- }
- case constant.NotificationChatType:
- sourceID = msgStruct.RecvID
- case constant.GroupChatType, constant.SuperGroupChatType:
- sourceID = msgStruct.GroupID
- }
- sessionType = msgStruct.SessionType
- msgIDs = append(msgIDs, msgStruct.ClientMsgID)
- }
- isExternalExtension = c.IsExternalExtensions
- localMessageList, err := c.db.GetMessagesByClientMsgIDs(ctx, conversationID, msgIDs)
- if err != nil {
- return nil, err
- }
- for _, v := range localMessageList {
- if v.IsReact != true {
- return nil, errors.New("have not reaction message in message list:" + v.ClientMsgID)
- }
- }
- var result server_api_params.GetMessageListReactionExtensionsResp
- extendMessage, _ := c.db.GetMultipleMessageReactionExtension(ctx, msgIDs)
- for _, v := range extendMessage {
- var singleResult server_api_params.SingleMessageExtensionResult
- // temp := make(map[string]*sdkws.KeyValue)
- // _ = json.Unmarshal(v.LocalReactionExtensions, &temp)
- singleResult.ClientMsgID = v.ClientMsgID
- // singleResult.ReactionExtensionList = temp
- result = append(result, &singleResult)
- }
- args := syncReactionExtensionParams{}
- args.MessageList = localMessageList
- args.SourceID = sourceID
- args.SessionType = sessionType
- args.ExtendMessageList = extendMessage
- args.IsExternalExtension = isExternalExtension
- _ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
- OperationID: "",
- Action: constant.SyncMessageListReactionExtensions,
- Args: args,
- }, c.GetCh())
- return result, nil
-
-}
-
-// funcation (c *Conversation) getMessageListSomeReactionExtensions(callback open_im_sdk_callback.Base, messageList []*sdk_struct.MsgStruct, keyList []string, operationID string) server_api_params.GetMessageListReactionExtensionsResp {
-// if len(messageList) == 0 {
-// common.CheckAnyErrCallback(callback, 201, errors.New("message list is null"), operationID)
-// }
-// var msgIDList []string
-// var sourceID string
-// var sessionType int32
-// var isExternalExtension bool
-// for _, msgStruct := range messageList {
-// switch msgStruct.SessionType {
-// case constant.SingleChatType:
-// if msgStruct.SendID == c.loginUserID {
-// sourceID = msgStruct.RecvID
-// } else {
-// sourceID = msgStruct.SendID
-// }
-// case constant.NotificationChatType:
-// sourceID = msgStruct.RecvID
-// case constant.GroupChatType, constant.SuperGroupChatType:
-// sourceID = msgStruct.GroupID
-// }
-// sessionType = msgStruct.SessionType
-// isExternalExtension = msgStruct.IsExternalExtensions
-// msgIDList = append(msgIDList, msgStruct.ClientMsgID)
-// }
-// localMessageList, err := c.db.GetMultipleMessageController(msgIDList, sourceID, sessionType)
-// common.CheckDBErrCallback(callback, err, operationID)
-// var result server_api_params.GetMessageListReactionExtensionsResp
-// extendMsgs, _ := c.db.GetMultipleMessageReactionExtension(msgIDList)
-// for _, v := range extendMsgs {
-// var singleResult server_api_params.SingleMessageExtensionResult
-// temp := make(map[string]*server_api_params.KeyValue)
-// _ = json.Unmarshal(v.LocalReactionExtensions, &temp)
-// for s, _ := range temp {
-// if !utils.IsContain(s, keyList) {
-// delete(temp, s)
-// }
-// }
-// singleResult.ClientMsgID = v.ClientMsgID
-// singleResult.ReactionExtensionList = temp
-// result = append(result, &singleResult)
-// }
-// args := syncReactionExtensionParams{}
-// args.MessageList = localMessageList
-// args.SourceID = sourceID
-// args.TypeKeyList = keyList
-// args.SessionType = sessionType
-// args.ExtendMessageList = extendMsgs
-// args.IsExternalExtension = isExternalExtension
-// _ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
-// OperationID: operationID,
-// Action: constant.SyncMessageListReactionExtensions,
-// Args: args,
-// }, c.GetCh())
-// return result
-// }
-//
-// funcation (c *Conversation) setTypeKeyInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, typeKey, ex string, isCanRepeat bool, operationID string) []*server_api_params.ExtensionResult {
-// message, err := c.db.GetMessageController(s)
-// common.CheckDBErrCallback(callback, err, operationID)
-// if message.Status != constant.MsgStatusSendSuccess {
-// common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
-// }
-// extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
-// temp := make(map[string]*server_api_params.KeyValue)
-// _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
-// var flag bool
-// var isContainSelfK string
-// var dbIsCanRepeat bool
-// var deletedKeyValue server_api_params.KeyValue
-// var maxTypeKey string
-// var maxTypeKeyValue server_api_params.KeyValue
-// reqTemp := make(map[string]*server_api_params.KeyValue)
-// for k, v := range temp {
-// if strings.HasPrefix(k, typeKey) {
-// flag = true
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// _ = json.Unmarshal([]byte(v.Value), singleTypeKeyInfo)
-// if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
-// isContainSelfK = k
-// dbIsCanRepeat = singleTypeKeyInfo.IsCanRepeat
-// delete(singleTypeKeyInfo.InfoList, c.loginUserID)
-// singleTypeKeyInfo.Counter--
-// deletedKeyValue.TypeKey = v.TypeKey
-// deletedKeyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
-// deletedKeyValue.LatestUpdateTime = v.LatestUpdateTime
-// }
-// if k > maxTypeKey {
-// maxTypeKey = k
-// maxTypeKeyValue = *v
-// }
-// }
-// }
-// if !flag {
-// if len(temp) >= 300 {
-// common.CheckAnyErrCallback(callback, 202, errors.New("number of keys only can support 300"), operationID)
-// }
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// singleTypeKeyInfo.TypeKey = getIndexTypeKey(typeKey, 0)
-// singleTypeKeyInfo.Counter = 1
-// singleTypeKeyInfo.IsCanRepeat = isCanRepeat
-// singleTypeKeyInfo.Index = 0
-// userInfo := new(sdk.Info)
-// userInfo.UserID = c.loginUserID
-// userInfo.Ex = ex
-// singleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
-// keyValue := new(server_api_params.KeyValue)
-// keyValue.TypeKey = singleTypeKeyInfo.TypeKey
-// keyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
-// reqTemp[singleTypeKeyInfo.TypeKey] = keyValue
-// } else {
-// if isContainSelfK != "" && !dbIsCanRepeat {
-// //删除操作
-// reqTemp[isContainSelfK] = &deletedKeyValue
-// } else {
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// _ = json.Unmarshal([]byte(maxTypeKeyValue.Value), singleTypeKeyInfo)
-// userInfo := new(sdk.Info)
-// userInfo.UserID = c.loginUserID
-// userInfo.Ex = ex
-// singleTypeKeyInfo.Counter++
-// singleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
-// maxTypeKeyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
-// data, _ := json.Marshal(maxTypeKeyValue)
-// if len(data) > 1000 { //单key超过了1kb
-// if len(temp) >= 300 {
-// common.CheckAnyErrCallback(callback, 202, errors.New("number of keys only can support 300"), operationID)
-// }
-// newSingleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// newSingleTypeKeyInfo.TypeKey = getIndexTypeKey(typeKey, singleTypeKeyInfo.Index+1)
-// newSingleTypeKeyInfo.Counter = 1
-// newSingleTypeKeyInfo.IsCanRepeat = singleTypeKeyInfo.IsCanRepeat
-// newSingleTypeKeyInfo.Index = singleTypeKeyInfo.Index + 1
-// userInfo := new(sdk.Info)
-// userInfo.UserID = c.loginUserID
-// userInfo.Ex = ex
-// newSingleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
-// keyValue := new(server_api_params.KeyValue)
-// keyValue.TypeKey = newSingleTypeKeyInfo.TypeKey
-// keyValue.Value = utils.StructToJsonString(newSingleTypeKeyInfo)
-// reqTemp[singleTypeKeyInfo.TypeKey] = keyValue
-// } else {
-// reqTemp[maxTypeKey] = &maxTypeKeyValue
-// }
-//
-// }
-// }
-// var sourceID string
-// switch message.SessionType {
-// case constant.SingleChatType:
-// sourceID = message.SendID + message.RecvID
-// case constant.NotificationChatType:
-// sourceID = message.RecvID
-// case constant.GroupChatType, constant.SuperGroupChatType:
-// sourceID = message.RecvID
-// }
-// var apiReq server_api_params.SetMessageReactionExtensionsReq
-// apiReq.IsReact = message.IsReact
-// apiReq.ClientMsgID = message.ClientMsgID
-// apiReq.SourceID = sourceID
-// apiReq.SessionType = message.SessionType
-// apiReq.IsExternalExtensions = message.IsExternalExtensions
-// apiReq.ReactionExtensionList = reqTemp
-// apiReq.OperationID = operationID
-// apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
-// var apiResp server_api_params.SetMessageReactionExtensionsResp
-// c.p.PostFatalCallback(callback, constant.SetMessageReactionExtensionsRouter, apiReq, &apiResp.ApiResult, apiReq.OperationID)
-// var msg model_struct.LocalChatLogReactionExtensions
-// msg.ClientMsgID = message.ClientMsgID
-// resultKeyMap := make(map[string]*server_api_params.KeyValue)
-// for _, v := range apiResp.ApiResult.Result {
-// if v.ErrCode == 0 {
-// temp := new(server_api_params.KeyValue)
-// temp.TypeKey = v.TypeKey
-// temp.Value = v.Value
-// temp.LatestUpdateTime = v.LatestUpdateTime
-// resultKeyMap[v.TypeKey] = temp
-// }
-// }
-// err = c.db.GetAndUpdateMessageReactionExtension(message.ClientMsgID, resultKeyMap)
-// if err != nil {
-// log.Error(operationID, "GetAndUpdateMessageReactionExtension err:", err.Error())
-// }
-// if !message.IsReact {
-// message.IsReact = apiResp.ApiResult.IsReact
-// message.MsgFirstModifyTime = apiResp.ApiResult.MsgFirstModifyTime
-// err = c.db.UpdateMessageController(message)
-// if err != nil {
-// log.Error(operationID, "UpdateMessageController err:", err.Error(), message)
-//
-// }
-// }
-// return apiResp.ApiResult.Result
-// }
-//
-// funcation getIndexTypeKey(typeKey string, index int) string {
-// return typeKey + "$" + utils.IntToString(index)
-// }
-func getPrefixTypeKey(typeKey string) string {
- list := strings.Split(typeKey, "$")
- if len(list) > 0 {
- return list[0]
- }
- return ""
-}
-
-//funcation (c *Conversation) getTypeKeyListInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, keyList []string, operationID string) (result []*sdk.SingleTypeKeyInfoSum) {
-// message, err := c.db.GetMessageController(s)
-// common.CheckDBErrCallback(callback, err, operationID)
-// if message.Status != constant.MsgStatusSendSuccess {
-// common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
-// }
-// if !message.IsReact {
-// common.CheckAnyErrCallback(callback, 202, errors.New("can get message reaction ex"), operationID)
-// }
-// extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
-// temp := make(map[string]*server_api_params.KeyValue)
-// _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
-// for _, v := range keyList {
-// singleResult := new(sdk.SingleTypeKeyInfoSum)
-// singleResult.TypeKey = v
-// for typeKey, value := range temp {
-// if strings.HasPrefix(typeKey, v) {
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// _ = json.Unmarshal([]byte(value.Value), singleTypeKeyInfo)
-// if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
-// singleResult.IsContainSelf = true
-// }
-// for _, info := range singleTypeKeyInfo.InfoList {
-// v := *info
-// singleResult.InfoList = append(singleResult.InfoList, &v)
-// }
-// singleResult.Counter += singleTypeKeyInfo.Counter
-// }
-// }
-// result = append(result, singleResult)
-// }
-// messageList := []*sdk_struct.MsgStruct{s}
-// _ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
-// OperationID: operationID,
-// Action: constant.SyncMessageListTypeKeyInfo,
-// Args: messageList,
-// }, c.GetCh())
-//
-// return result
-//}
-//
-//funcation (c *Conversation) getAllTypeKeyInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, operationID string) (result []*sdk.SingleTypeKeyInfoSum) {
-// message, err := c.db.GetMessageController(s)
-// common.CheckDBErrCallback(callback, err, operationID)
-// if message.Status != constant.MsgStatusSendSuccess {
-// common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
-// }
-// if !message.IsReact {
-// common.CheckAnyErrCallback(callback, 202, errors.New("can get message reaction ex"), operationID)
-// }
-// extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
-// temp := make(map[string]*server_api_params.KeyValue)
-// _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
-// mapResult := make(map[string]*sdk.SingleTypeKeyInfoSum)
-// for typeKey, value := range temp {
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// err := json.Unmarshal([]byte(value.Value), singleTypeKeyInfo)
-// if err != nil {
-// log.Warn(operationID, "not this type ", value.Value)
-// continue
-// }
-// prefixKey := getPrefixTypeKey(typeKey)
-// if v, ok := mapResult[prefixKey]; ok {
-// for _, info := range singleTypeKeyInfo.InfoList {
-// t := *info
-// v.InfoList = append(v.InfoList, &t)
-// }
-// if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
-// v.IsContainSelf = true
-// }
-// v.Counter += singleTypeKeyInfo.Counter
-// } else {
-// v := new(sdk.SingleTypeKeyInfoSum)
-// v.TypeKey = prefixKey
-// v.Counter = singleTypeKeyInfo.Counter
-// for _, info := range singleTypeKeyInfo.InfoList {
-// t := *info
-// v.InfoList = append(v.InfoList, &t)
-// }
-// if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
-// v.IsContainSelf = true
-// }
-// mapResult[prefixKey] = v
-// }
-// }
-// for _, v := range mapResult {
-// result = append(result, v)
-//
-// }
-// return result
-//}
diff --git a/internal/conversation_msg/conversation_msg.go b/internal/conversation_msg/conversation_msg.go
index 6167241fa..2647463fd 100644
--- a/internal/conversation_msg/conversation_msg.go
+++ b/internal/conversation_msg/conversation_msg.go
@@ -1,32 +1,21 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
package conversation_msg
import (
"context"
"encoding/json"
"errors"
+ "fmt"
"math"
+ "sync"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/cache"
+ "github.com/openimsdk/tools/utils/stringutil"
- "github.com/openimsdk/openim-sdk-core/v3/internal/business"
- "github.com/openimsdk/openim-sdk-core/v3/internal/cache"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
- "github.com/openimsdk/openim-sdk-core/v3/internal/friend"
- "github.com/openimsdk/openim-sdk-core/v3/internal/full"
"github.com/openimsdk/openim-sdk-core/v3/internal/group"
"github.com/openimsdk/openim-sdk-core/v3/internal/interaction"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/relation"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
"github.com/openimsdk/openim-sdk-core/v3/internal/user"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
@@ -35,11 +24,10 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/page"
- sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
pbConversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
@@ -53,33 +41,38 @@ import (
)
const (
- conversationSyncLimit = math.MaxInt64
+ conversationSyncLimit int64 = math.MaxInt64
+ searchMessageGoroutineLimit int = 10
)
var SearchContentType = []int{constant.Text, constant.AtText, constant.File}
type Conversation struct {
*interaction.LongConnMgr
- conversationSyncer *syncer.Syncer[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string]
- db db_interface.DataBase
- ConversationListener func() open_im_sdk_callback.OnConversationListener
- msgListener func() open_im_sdk_callback.OnAdvancedMsgListener
- msgKvListener func() open_im_sdk_callback.OnMessageKvInfoListener
- batchMsgListener func() open_im_sdk_callback.OnBatchMsgListener
- recvCH chan common.Cmd2Value
- loginUserID string
- platformID int32
- DataDir string
- friend *friend.Friend
- group *group.Group
- user *user.User
- file *file.File
- business *business.Business
- messageController *MessageController
- cache *cache.Cache[string, *model_struct.LocalConversation]
- full *full.Full
- maxSeqRecorder MaxSeqRecorder
- IsExternalExtensions bool
+ conversationSyncer *syncer.Syncer[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string]
+ db db_interface.DataBase
+ ConversationListener func() open_im_sdk_callback.OnConversationListener
+ msgListener func() open_im_sdk_callback.OnAdvancedMsgListener
+ msgKvListener func() open_im_sdk_callback.OnMessageKvInfoListener
+ businessListener func() open_im_sdk_callback.OnCustomBusinessListener
+ recvCh chan common.Cmd2Value
+ msgSyncerCh chan common.Cmd2Value
+ loginUserID string
+ platformID int32
+ DataDir string
+ relation *relation.Relation
+ group *group.Group
+ user *user.User
+ file *file.File
+ cache *cache.Cache[string, *model_struct.LocalConversation]
+ maxSeqRecorder MaxSeqRecorder
+ messagePullForwardEndSeqMap *cache.ConversationSeqContextCache
+ messagePullReverseEndSeqMap *cache.ConversationSeqContextCache
+ IsExternalExtensions bool
+ msgOffset int
+ progress int
+ conversationSyncMutex sync.Mutex
+ streamMsgMutex sync.Mutex
startTime time.Time
@@ -94,29 +87,31 @@ func (c *Conversation) SetMsgKvListener(msgKvListener func() open_im_sdk_callbac
c.msgKvListener = msgKvListener
}
-func (c *Conversation) SetBatchMsgListener(batchMsgListener func() open_im_sdk_callback.OnBatchMsgListener) {
- c.batchMsgListener = batchMsgListener
+func (c *Conversation) SetBusinessListener(businessListener func() open_im_sdk_callback.OnCustomBusinessListener) {
+ c.businessListener = businessListener
}
func NewConversation(ctx context.Context, longConnMgr *interaction.LongConnMgr, db db_interface.DataBase,
- ch chan common.Cmd2Value, friend *friend.Friend, group *group.Group, user *user.User, business *business.Business,
- full *full.Full, file *file.File) *Conversation {
+ recvCh, msgSyncerCh chan common.Cmd2Value, relation *relation.Relation, group *group.Group, user *user.User,
+ file *file.File) *Conversation {
info := ccontext.Info(ctx)
n := &Conversation{db: db,
- LongConnMgr: longConnMgr,
- recvCH: ch,
- loginUserID: info.UserID(),
- platformID: info.PlatformID(),
- DataDir: info.DataDir(),
- friend: friend,
- group: group,
- user: user,
- full: full,
- business: business,
- file: file,
- messageController: NewMessageController(db, ch),
- IsExternalExtensions: info.IsExternalExtensions(),
- maxSeqRecorder: NewMaxSeqRecorder(),
+ LongConnMgr: longConnMgr,
+ recvCh: recvCh,
+ msgSyncerCh: msgSyncerCh,
+ loginUserID: info.UserID(),
+ platformID: info.PlatformID(),
+ DataDir: info.DataDir(),
+ relation: relation,
+ group: group,
+ user: user,
+ file: file,
+ IsExternalExtensions: info.IsExternalExtensions(),
+ maxSeqRecorder: NewMaxSeqRecorder(),
+ messagePullForwardEndSeqMap: cache.NewConversationSeqContextCache(),
+ messagePullReverseEndSeqMap: cache.NewConversationSeqContextCache(),
+ msgOffset: 0,
+ progress: 0,
}
n.typing = newTyping(n)
n.initSyncer()
@@ -143,7 +138,7 @@ func (c *Conversation) initSyncer() {
"update_unread_count_time": serverConversation.UpdateUnreadCountTime,
"attached_info": serverConversation.AttachedInfo, "ex": serverConversation.Ex, "msg_destruct_time": serverConversation.MsgDestructTime,
"is_msg_destruct": serverConversation.IsMsgDestruct,
- "max_seq": serverConversation.MaxSeq, "min_seq": serverConversation.MinSeq, "has_read_seq": serverConversation.HasReadSeq})
+ "max_seq": serverConversation.MaxSeq, "min_seq": serverConversation.MinSeq})
}),
syncer.WithUUID[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string](func(value *model_struct.LocalConversation) string {
return value.ConversationID
@@ -189,14 +184,14 @@ func (c *Conversation) initSyncer() {
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string](func(resp *pbConversation.GetOwnerConversationResp) []*model_struct.LocalConversation {
return datautil.Batch(ServerConversationToLocal, resp.Conversations)
}),
- syncer.WithReqApiRouter[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string](constant.GetOwnerConversationRouter),
+ syncer.WithReqApiRouter[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string](api.GetOwnerConversation.Route()),
syncer.WithFullSyncLimit[*model_struct.LocalConversation, pbConversation.GetOwnerConversationResp, string](conversationSyncLimit),
)
}
func (c *Conversation) GetCh() chan common.Cmd2Value {
- return c.recvCH
+ return c.recvCh
}
type onlineMsgKey struct {
@@ -210,58 +205,69 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
var isTriggerUnReadCount bool
insertMsg := make(map[string][]*model_struct.LocalChatLog, 10)
updateMsg := make(map[string][]*model_struct.LocalChatLog, 10)
- var exceptionMsg []*model_struct.LocalErrChatLog
- //var unreadMessages []*model_struct.LocalConversationUnreadMessage
+ var exceptionMsg []*model_struct.LocalChatLog
var newMessages sdk_struct.NewMsgList
- // var reactionMsgModifierList, reactionMsgDeleterList sdk_struct.NewMsgList
+
var isUnreadCount, isConversationUpdate, isHistory, isNotPrivate, isSenderConversationUpdate bool
+
conversationChangedSet := make(map[string]*model_struct.LocalConversation)
newConversationSet := make(map[string]*model_struct.LocalConversation)
conversationSet := make(map[string]*model_struct.LocalConversation)
phConversationChangedSet := make(map[string]*model_struct.LocalConversation)
phNewConversationSet := make(map[string]*model_struct.LocalConversation)
+
log.ZDebug(ctx, "message come here conversation ch", "conversation length", len(allMsg))
b := time.Now()
+
onlineMap := make(map[onlineMsgKey]struct{})
+
for conversationID, msgs := range allMsg {
log.ZDebug(ctx, "parse message in one conversation", "conversationID",
conversationID, "message length", len(msgs.Msgs))
var insertMessage, selfInsertMessage, othersInsertMessage []*model_struct.LocalChatLog
var updateMessage []*model_struct.LocalChatLog
+
for _, v := range msgs.Msgs {
log.ZDebug(ctx, "parse message ", "conversationID", conversationID, "msg", v)
isHistory = utils.GetSwitchFromOptions(v.Options, constant.IsHistory)
+
isUnreadCount = utils.GetSwitchFromOptions(v.Options, constant.IsUnreadCount)
+
isConversationUpdate = utils.GetSwitchFromOptions(v.Options, constant.IsConversationUpdate)
+
isNotPrivate = utils.GetSwitchFromOptions(v.Options, constant.IsNotPrivate)
+
isSenderConversationUpdate = utils.GetSwitchFromOptions(v.Options, constant.IsSenderConversationUpdate)
+
msg := &sdk_struct.MsgStruct{}
copier.Copy(msg, v)
msg.Content = string(v.Content)
+
var attachedInfo sdk_struct.AttachedInfoElem
_ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
msg.AttachedInfoElem = &attachedInfo
+ //When the message has been marked and deleted by the cloud, it is directly inserted locally without any conversation and message update.
+ if msg.Status == constant.MsgStatusHasDeleted {
+ dbMessage := MsgStructToLocalChatLog(msg)
+ c.handleExceptionMessages(ctx, nil, dbMessage)
+ exceptionMsg = append(exceptionMsg, dbMessage)
+ insertMessage = append(insertMessage, dbMessage)
+ continue
+ }
+
msg.Status = constant.MsgStatusSendSuccess
- // msg.IsRead = false
+
//De-analyze data
- err := c.msgHandleByContentType(msg)
+ err := msgHandleByContentType(msg)
if err != nil {
log.ZError(ctx, "Parsing data error:", err, "type: ", msg.ContentType, "msg", msg)
continue
}
- //When the message has been marked and deleted by the cloud, it is directly inserted locally without any conversation and message update.
- if msg.Status == constant.MsgStatusHasDeleted {
- insertMessage = append(insertMessage, c.msgStructToLocalChatLog(msg))
- continue
- }
+
if !isNotPrivate {
msg.AttachedInfoElem.IsPrivateChat = true
}
- if msg.ClientMsgID == "" {
- exceptionMsg = append(exceptionMsg, c.msgStructToLocalErrChatLog(msg))
- continue
- }
if conversationID == "" {
log.ZError(ctx, "conversationID is empty", errors.New("conversationID is empty"), "msg", msg)
continue
@@ -274,16 +280,19 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
log.ZDebug(ctx, "decode message", "msg", msg)
if v.SendID == c.loginUserID { //seq
// Messages sent by myself //if sent through this terminal
- m, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID)
+ existingMsg, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID)
if err == nil {
log.ZInfo(ctx, "have message", "msg", msg)
- if m.Seq == 0 {
+ if existingMsg.Seq == 0 {
if !isConversationUpdate {
msg.Status = constant.MsgStatusFiltered
}
- updateMessage = append(updateMessage, c.msgStructToLocalChatLog(msg))
+ updateMessage = append(updateMessage, MsgStructToLocalChatLog(msg))
} else {
- exceptionMsg = append(exceptionMsg, c.msgStructToLocalErrChatLog(msg))
+ dbMessage := MsgStructToLocalChatLog(msg)
+ c.handleExceptionMessages(ctx, existingMsg, dbMessage)
+ insertMessage = append(insertMessage, dbMessage)
+ exceptionMsg = append(exceptionMsg, dbMessage)
}
} else {
log.ZInfo(ctx, "sync message", "msg", msg)
@@ -296,7 +305,7 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
switch v.SessionType {
case constant.SingleChatType:
lc.UserID = v.RecvID
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
lc.GroupID = v.GroupID
}
if isConversationUpdate {
@@ -307,11 +316,11 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
newMessages = append(newMessages, msg)
}
if isHistory {
- selfInsertMessage = append(selfInsertMessage, c.msgStructToLocalChatLog(msg))
+ selfInsertMessage = append(selfInsertMessage, MsgStructToLocalChatLog(msg))
}
}
} else { //Sent by others
- if _, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID); err != nil { //Deduplication operation
+ if existingMsg, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID); err != nil {
lc := model_struct.LocalConversation{
ConversationType: v.SessionType,
LatestMsg: utils.StructToJsonString(msg),
@@ -323,7 +332,7 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
lc.UserID = v.SendID
lc.ShowName = msg.SenderNickname
lc.FaceURL = msg.SenderFaceURL
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
lc.GroupID = v.GroupID
case constant.NotificationChatType:
lc.UserID = v.SendID
@@ -341,15 +350,14 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
newMessages = append(newMessages, msg)
}
if isHistory {
- othersInsertMessage = append(othersInsertMessage, c.msgStructToLocalChatLog(msg))
+ othersInsertMessage = append(othersInsertMessage, MsgStructToLocalChatLog(msg))
}
} else {
- exceptionMsg = append(exceptionMsg, c.msgStructToLocalErrChatLog(msg))
- log.ZWarn(ctx, "Deduplication operation ", nil, "msg", *c.msgStructToLocalErrChatLog(msg))
- msg.Status = constant.MsgStatusFiltered
- msg.ClientMsgID = msg.ClientMsgID + utils.Int64ToString(utils.GetCurrentTimestampByNano())
- othersInsertMessage = append(othersInsertMessage, c.msgStructToLocalChatLog(msg))
+ dbMessage := MsgStructToLocalChatLog(msg)
+ c.handleExceptionMessages(ctx, existingMsg, dbMessage)
+ insertMessage = append(insertMessage, dbMessage)
+ exceptionMsg = append(exceptionMsg, dbMessage)
}
}
}
@@ -359,23 +367,32 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
}
}
+
+ //todo The lock granularity needs to be optimized to the conversation level.
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
+
list, err := c.db.GetAllConversationListDB(ctx)
if err != nil {
log.ZError(ctx, "GetAllConversationListDB", err)
}
+
m := make(map[string]*model_struct.LocalConversation)
listToMap(list, m)
- log.ZDebug(ctx, "listToMap: ", "local conversation", list, "generated c map", conversationSet)
+ log.ZDebug(ctx, "listToMap: ", "local conversation", list, "generated c map",
+ string(stringutil.StructToJsonBytes(conversationSet)))
+
c.diff(ctx, m, conversationSet, conversationChangedSet, newConversationSet)
- log.ZInfo(ctx, "trigger map is :", "newConversations", newConversationSet, "changedConversations", conversationChangedSet)
+ log.ZInfo(ctx, "trigger map is :", "newConversations", string(stringutil.StructToJsonBytes(newConversationSet)),
+ "changedConversations", string(stringutil.StructToJsonBytes(conversationChangedSet)))
//seq sync message update
- if err := c.messageController.BatchUpdateMessageList(ctx, updateMsg); err != nil {
+ if err := c.batchUpdateMessageList(ctx, updateMsg); err != nil {
log.ZError(ctx, "sync seq normal message err :", err)
}
//Normal message storage
- _ = c.messageController.BatchInsertMessageList(ctx, insertMsg)
+ _ = c.batchInsertMessageList(ctx, insertMsg)
hList, _ := c.db.GetHiddenConversationList(ctx)
for _, v := range hList {
@@ -415,11 +432,8 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
}
log.ZDebug(ctx, "before trigger msg", "cost time", time.Since(b).Seconds(), "len", len(allMsg))
- if c.batchMsgListener() != nil {
- c.batchNewMessages(ctx, newMessages)
- } else {
- c.newMessage(ctx, newMessages, conversationChangedSet, newConversationSet, onlineMap)
- }
+ c.newMessage(ctx, newMessages, conversationChangedSet, newConversationSet, onlineMap)
+
if len(newConversationSet) > 0 {
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.NewConDirect, Args: utils.StructToJsonString(mapConversationToList(newConversationSet))}})
}
@@ -438,15 +452,127 @@ func (c *Conversation) doMsgNew(c2v common.Cmd2Value) {
}
}
}
- log.ZDebug(ctx, "insert msg", "cost time", time.Since(b).Seconds(), "len", len(allMsg))
+ //Exception message storage
+ for _, v := range exceptionMsg {
+ log.ZWarn(ctx, "exceptionMsg show: ", nil, "msg", *v)
+ }
+
+ log.ZDebug(ctx, "insert msg", "duration", fmt.Sprintf("%dms", time.Since(b)), "len", len(allMsg))
+}
+
+func (c *Conversation) doMsgSyncByReinstalled(c2v common.Cmd2Value) {
+ allMsg := c2v.Value.(sdk_struct.CmdMsgSyncInReinstall).Msgs
+ ctx := c2v.Ctx
+ msgLen := len(allMsg)
+ c.msgOffset += msgLen
+ total := c2v.Value.(sdk_struct.CmdMsgSyncInReinstall).Total
+
+ insertMsg := make(map[string][]*model_struct.LocalChatLog, 10)
+ conversationList := make([]*model_struct.LocalConversation, 0)
+ var exceptionMsg []*model_struct.LocalChatLog
+
+ log.ZDebug(ctx, "message come here conversation ch in reinstalled", "conversation length", msgLen)
+ b := time.Now()
+
+ for conversationID, msgs := range allMsg {
+ log.ZDebug(ctx, "parse message in one conversation", "conversationID",
+ conversationID, "message length", len(msgs.Msgs))
+ var insertMessage, selfInsertMessage, othersInsertMessage []*model_struct.LocalChatLog
+ var latestMsg *sdk_struct.MsgStruct
+ if len(msgs.Msgs) == 0 {
+ log.ZWarn(ctx, "msg.Msgs is empty", errs.New("msg.Msgs is empty"), "conversationID", conversationID)
+ continue
+ }
+ for _, v := range msgs.Msgs {
+
+ log.ZDebug(ctx, "parse message ", "conversationID", conversationID, "msg", v)
+ msg := &sdk_struct.MsgStruct{}
+ // TODO need replace when after.
+ copier.Copy(msg, v)
+ msg.Content = string(v.Content)
+ var attachedInfo sdk_struct.AttachedInfoElem
+ _ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
+ msg.AttachedInfoElem = &attachedInfo
+
+ //When the message has been marked and deleted by the cloud, it is directly inserted locally without any conversation and message update.
+ if msg.Status == constant.MsgStatusHasDeleted {
+ dbMessage := MsgStructToLocalChatLog(msg)
+ c.handleExceptionMessages(ctx, nil, dbMessage)
+ exceptionMsg = append(exceptionMsg, dbMessage)
+ insertMessage = append(insertMessage, dbMessage)
+ continue
+ }
+ msg.Status = constant.MsgStatusSendSuccess
+
+ err := msgHandleByContentType(msg)
+ if err != nil {
+ log.ZError(ctx, "Parsing data error:", err, "type: ", msg.ContentType, "msg", msg)
+ continue
+ }
+
+ if conversationID == "" {
+ log.ZError(ctx, "conversationID is empty", errors.New("conversationID is empty"), "msg", msg)
+ continue
+ }
+
+ log.ZDebug(ctx, "decode message", "msg", msg)
+ if v.SendID == c.loginUserID {
+ // Messages sent by myself //if sent through this terminal
+ log.ZInfo(ctx, "sync message in reinstalled", "msg", msg)
+
+ latestMsg = msg
+
+ selfInsertMessage = append(selfInsertMessage, MsgStructToLocalChatLog(msg))
+ } else { //Sent by others
+ othersInsertMessage = append(othersInsertMessage, MsgStructToLocalChatLog(msg))
+
+ latestMsg = msg
+ }
+ }
+
+ if latestMsg != nil {
+ conversationList = append(conversationList, &model_struct.LocalConversation{
+ LatestMsg: utils.StructToJsonString(latestMsg),
+ LatestMsgSendTime: latestMsg.SendTime,
+ ConversationID: conversationID,
+ })
+ } else {
+ log.ZWarn(ctx, "latestMsg is nil", errs.New("latestMsg is nil"), "conversationID", conversationID)
+ }
+
+ insertMsg[conversationID] = append(insertMessage, c.faceURLAndNicknameHandle(ctx, selfInsertMessage, othersInsertMessage, conversationID)...)
+ }
+
+ // message storage
+ _ = c.batchInsertMessageList(ctx, insertMsg)
+
+ // conversation storage
+ if err := c.db.BatchUpdateConversationList(ctx, conversationList); err != nil {
+ log.ZError(ctx, "insert new conversation err:", err)
+ }
+ log.ZDebug(ctx, "before trigger msg", "cost time", time.Since(b).Seconds(), "len", len(allMsg))
+
+ // log.ZDebug(ctx, "progress is", "msgLen", msgLen, "msgOffset", c.msgOffset, "total", total, "now progress is", (c.msgOffset*(100-InitSyncProgress))/total + InitSyncProgress)
+ c.ConversationListener().OnSyncServerProgress((c.msgOffset*(100-InitSyncProgress))/total + InitSyncProgress)
+ //Exception message storage
+ for _, v := range exceptionMsg {
+ log.ZWarn(ctx, "exceptionMsg show: ", nil, "msg", *v)
+ }
+}
+
+func (c *Conversation) addInitProgress(progress int) {
+ c.progress += progress
+ if c.progress > 100 {
+ c.progress = 100
+ }
}
func listToMap(list []*model_struct.LocalConversation, m map[string]*model_struct.LocalConversation) {
for _, v := range list {
m[v.ConversationID] = v
}
-
}
+
func (c *Conversation) diff(ctx context.Context, local, generated, cc, nc map[string]*model_struct.LocalConversation) {
var newConversations []*model_struct.LocalConversation
for _, v := range generated {
@@ -474,6 +600,7 @@ func (c *Conversation) diff(ctx context.Context, local, generated, cc, nc map[st
}
}
}
+
func (c *Conversation) genConversationGroupAtType(lc *model_struct.LocalConversation, s *sdk_struct.MsgStruct) {
if s.ContentType == constant.AtText {
tagMe := utils.IsContain(c.loginUserID, s.AtTextElem.AtUserList)
@@ -492,182 +619,76 @@ func (c *Conversation) genConversationGroupAtType(lc *model_struct.LocalConversa
}
}
-func (c *Conversation) msgStructToLocalErrChatLog(m *sdk_struct.MsgStruct) *model_struct.LocalErrChatLog {
- var lc model_struct.LocalErrChatLog
- copier.Copy(&lc, m)
- if m.SessionType == constant.GroupChatType || m.SessionType == constant.SuperGroupChatType {
- lc.RecvID = m.GroupID
+func (c *Conversation) batchUpdateMessageList(ctx context.Context, updateMsg map[string][]*model_struct.LocalChatLog) error {
+ if updateMsg == nil {
+ return nil
}
- return &lc
-}
-
-func (c *Conversation) tempCacheChatLog(ctx context.Context, messageList []*sdk_struct.MsgStruct) {
- var newMessageList []*model_struct.TempCacheLocalChatLog
- copier.Copy(&newMessageList, &messageList)
- if err := c.db.BatchInsertTempCacheMessageList(ctx, newMessageList); err != nil {
- // log.Error("", "BatchInsertTempCacheMessageList detail err:", err.Error(), len(newMessageList))
- for _, v := range newMessageList {
- err := c.db.InsertTempCacheMessage(ctx, v)
+ for conversationID, messages := range updateMsg {
+ conversation, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZError(ctx, "GetConversation err", err, "conversationID", conversationID)
+ continue
+ }
+ latestMsg := &sdk_struct.MsgStruct{}
+ if err := json.Unmarshal([]byte(conversation.LatestMsg), latestMsg); err != nil {
+ log.ZError(ctx, "Unmarshal err", err, "conversationID",
+ conversationID, "latestMsg", conversation.LatestMsg, "messages", messages)
+ continue
+ }
+ for _, v := range messages {
+ v1 := new(model_struct.LocalChatLog)
+ v1.ClientMsgID = v.ClientMsgID
+ v1.Seq = v.Seq
+ v1.Status = v.Status
+ v1.RecvID = v.RecvID
+ v1.SessionType = v.SessionType
+ v1.ServerMsgID = v.ServerMsgID
+ v1.SendTime = v.SendTime
+ err := c.db.UpdateMessage(ctx, conversationID, v1)
if err != nil {
- log.ZWarn(ctx, "InsertTempCacheMessage operation", err, "chat err log: ", *v)
+ return errs.WrapMsg(err, "BatchUpdateMessageList failed")
+ }
+ if latestMsg.ClientMsgID == v.ClientMsgID {
+ latestMsg.ServerMsgID = v.ServerMsgID
+ latestMsg.Seq = v.Seq
+ latestMsg.SendTime = v.SendTime
+ latestMsg.Status = v.Status
+ conversation.LatestMsg = utils.StructToJsonString(latestMsg)
+
+ c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversation.ConversationID,
+ Action: constant.AddConOrUpLatMsg, Args: *conversation}})
+
}
}
+
}
+ return nil
}
-func (c *Conversation) DoMsgReaction(msgReactionList []*sdk_struct.MsgStruct) {
+func (c *Conversation) batchInsertMessageList(ctx context.Context, insertMsg map[string][]*model_struct.LocalChatLog) error {
+ if insertMsg == nil {
+ return nil
+ }
+ for conversationID, messages := range insertMsg {
+ if len(messages) == 0 {
+ continue
+ }
+ err := c.db.BatchInsertMessageList(ctx, conversationID, messages)
+ if err != nil {
+ log.ZError(ctx, "BatchInsertMessageList detail err:", err, "conversationID", conversationID, "messages", messages)
+ for _, v := range messages {
+ e := c.db.InsertMessage(ctx, conversationID, v)
+ if e != nil {
+ log.ZError(ctx, "InsertMessage err", err, "conversationID", conversationID, "message", v)
+ }
+ }
+ }
- //for _, v := range msgReactionList {
- // var msg sdk_struct.MessageReaction
- // err := json.Unmarshal([]byte(v.Content), &msg)
- // if err != nil {
- // log.Error("internal", "unmarshal failed, err : ", err.Error(), *v)
- // continue
- // }
- // s := sdk_struct.MsgStruct{GroupID: msg.GroupID, ClientMsgID: msg.ClientMsgID, SessionType: msg.SessionType}
- // message, err := c.db.GetMessageController(&s)
- // if err != nil {
- // log.Error("internal", "GetMessageController, err : ", err.Error(), s)
- // continue
- // }
- // t := new(model_struct.LocalChatLog)
- // attachInfo := sdk_struct.AttachedInfoElem{}
- // _ = utils.JsonStringToStruct(message.AttachedInfo, &attachInfo)
- //
- // contain, v := isContainMessageReaction(msg.ReactionType, attachInfo.MessageReactionElem)
- // if contain {
- // userContain, userReaction := isContainUserReactionElem(msg.UserID, v.UserReactionList)
- // if userContain {
- // if !v.CanRepeat && userReaction.Counter > 0 {
- // // to do nothing
- // continue
- // } else {
- // userReaction.Counter += msg.Counter
- // v.Counter += msg.Counter
- // if v.Counter < 0 {
- // log.Debug("internal", "after operate all counter < 0", v.Type, v.Counter, msg.Counter)
- // v.Counter = 0
- // }
- // if userReaction.Counter <= 0 {
- // log.Debug("internal", "after operate userReaction counter < 0", v.Type, userReaction.Counter, msg.Counter)
- // v.UserReactionList = DeleteUserReactionElem(v.UserReactionList, c.loginUserID)
- // }
- // }
- // } else {
- // log.Debug("internal", "attachInfo.MessageReactionElem is nil", msg)
- // u := new(sdk_struct.UserReactionElem)
- // u.UserID = msg.UserID
- // u.Counter = msg.Counter
- // v.Counter += msg.Counter
- // if v.Counter < 0 {
- // log.Debug("internal", "after operate all counter < 0", v.Type, v.Counter, msg.Counter)
- // v.Counter = 0
- // }
- // if u.Counter <= 0 {
- // log.Debug("internal", "after operate userReaction counter < 0", v.Type, u.Counter, msg.Counter)
- // v.UserReactionList = DeleteUserReactionElem(v.UserReactionList, msg.UserID)
- // }
- // v.UserReactionList = append(v.UserReactionList, u)
- //
- // }
- //
- // } else {
- // log.Debug("internal", "attachInfo.MessageReactionElem is nil", msg)
- // t := new(sdk_struct.ReactionElem)
- // t.Counter = msg.Counter
- // t.Type = msg.ReactionType
- // u := new(sdk_struct.UserReactionElem)
- // u.UserID = msg.UserID
- // u.Counter = msg.Counter
- // t.UserReactionList = append(t.UserReactionList, u)
- // attachInfo.MessageReactionElem = append(attachInfo.MessageReactionElem, t)
- //
- // }
- //
- // t.AttachedInfo = utils.StructToJsonString(attachInfo)
- // t.ClientMsgID = message.ClientMsgID
- //
- // t.SessionType = message.SessionType
- // t.RecvID = message.RecvID
- // err1 := c.db.UpdateMessageController(t)
- // if err1 != nil {
- // log.Error("internal", "UpdateMessageController err:", err1, "ClientMsgID", *t, message)
- // }
- // c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{"", constant.MessageChange, &s}})
- //
- //}
+ }
+ return nil
}
-// func (c *Conversation) doReactionMsgModifier(ctx context.Context, msgReactionList []*sdk_struct.MsgStruct) {
-// for _, msgStruct := range msgReactionList {
-// var n server_api_params.ReactionMessageModifierNotification
-// err := json.Unmarshal([]byte(msgStruct.Content), &n)
-// if err != nil {
-// // log.Error("internal", "unmarshal failed err:", err.Error(), *msgStruct)
-// continue
-// }
-// switch n.Operation {
-// case constant.AddMessageExtensions:
-// var reactionExtensionList []*sdkws.KeyValue
-// for _, value := range n.SuccessReactionExtensionList {
-// reactionExtensionList = append(reactionExtensionList, value)
-// }
-// if !(msgStruct.SendID == c.loginUserID && msgStruct.SenderPlatformID == c.platformID) {
-// c.msgListener.OnRecvMessageExtensionsAdded(n.ClientMsgID, utils.StructToJsonString(reactionExtensionList))
-// }
-// case constant.SetMessageExtensions:
-// err = c.db.GetAndUpdateMessageReactionExtension(ctx, n.ClientMsgID, n.SuccessReactionExtensionList)
-// if err != nil {
-// // log.Error("internal", "GetAndUpdateMessageReactionExtension err:", err.Error())
-// continue
-// }
-// var reactionExtensionList []*sdkws.KeyValue
-// for _, value := range n.SuccessReactionExtensionList {
-// reactionExtensionList = append(reactionExtensionList, value)
-// }
-// if !(msgStruct.SendID == c.loginUserID && msgStruct.SenderPlatformID == c.platformID) {
-// c.msgListener.OnRecvMessageExtensionsChanged(n.ClientMsgID, utils.StructToJsonString(reactionExtensionList))
-// }
-
-// }
-// t := model_struct.LocalChatLog{}
-// t.ClientMsgID = n.ClientMsgID
-// t.SessionType = n.SessionType
-// t.IsExternalExtensions = n.IsExternalExtensions
-// t.IsReact = n.IsReact
-// t.MsgFirstModifyTime = n.MsgFirstModifyTime
-// if n.SessionType == constant.GroupChatType || n.SessionType == constant.SuperGroupChatType {
-// t.RecvID = n.SourceID
-// }
-// //todo
-// err2 := c.db.UpdateMessage(ctx, "", &t)
-// if err2 != nil {
-// // log.Error("internal", "unmarshal failed err:", err2.Error(), t)
-// continue
-// }
-// }
-// }
-
-func (c *Conversation) doReactionMsgDeleter(ctx context.Context, msgReactionList []*sdk_struct.MsgStruct) {
- // for _, msgStruct := range msgReactionList {
- // var n server_api_params.ReactionMessageDeleteNotification
- // err := json.Unmarshal([]byte(msgStruct.Content), &n)
- // if err != nil {
- // // log.Error("internal", "unmarshal failed err:", err.Error(), *msgStruct)
- // continue
- // }
- // err = c.db.DeleteAndUpdateMessageReactionExtension(ctx, n.ClientMsgID, n.SuccessReactionExtensionList)
- // if err != nil {
- // // log.Error("internal", "GetAndUpdateMessageReactionExtension err:", err.Error())
- // continue
- // }
- // var deleteKeyList []string
- // for _, value := range n.SuccessReactionExtensionList {
- // deleteKeyList = append(deleteKeyList, value.TypeKey)
- // }
- // c.msgListener.OnRecvMessageExtensionsDeleted(n.ClientMsgID, utils.StructToJsonString(deleteKeyList))
-
- // }
+func (c *Conversation) DoMsgReaction(msgReactionList []*sdk_struct.MsgStruct) {
}
func (c *Conversation) newMessage(ctx context.Context, newMessagesList sdk_struct.NewMsgList, cc, nc map[string]*model_struct.LocalConversation, onlineMsg map[onlineMsgKey]struct{}) {
@@ -704,172 +725,54 @@ func (c *Conversation) newMessage(ctx context.Context, newMessagesList sdk_struc
}
}
-func (c *Conversation) batchNewMessages(ctx context.Context, newMessagesList sdk_struct.NewMsgList) {
- sort.Sort(newMessagesList)
- if len(newMessagesList) > 0 {
- c.batchMsgListener().OnRecvNewMessages(utils.StructToJsonString(newMessagesList))
- //if c.IsBackground {
- // c.batchMsgListener.OnRecvOfflineNewMessages(utils.StructToJsonString(newMessagesList))
- //}
+func (c *Conversation) batchNewMessages(ctx context.Context, newMessagesList sdk_struct.NewMsgList, conversationChanged, newConversation map[string]*model_struct.LocalConversation, onlineMsg map[onlineMsgKey]struct{}) {
+ if len(newMessagesList) == 0 {
+ log.ZWarn(ctx, "newMessagesList is empty", errs.New("newMessagesList is empty"))
+ return
}
-}
-
-func (c *Conversation) doMsgReadState(ctx context.Context, msgReadList []*sdk_struct.MsgStruct) {
- var messageReceiptResp []*sdk_struct.MessageReceipt
- var msgIdList []string
- chrsList := make(map[string][]string)
- var conversationID string
+ sort.Sort(newMessagesList)
+ var needNotificationMsgList sdk_struct.NewMsgList
- for _, rd := range msgReadList {
- err := json.Unmarshal([]byte(rd.Content), &msgIdList)
+ // offline
+ if c.GetBackground() {
+ u, err := c.user.GetSelfUserInfo(ctx)
if err != nil {
- // log.Error("internal", "unmarshal failed, err : ", err.Error())
- return
- }
- var msgIdListStatusOK []string
- for _, v := range msgIdList {
- //m, err := c.db.GetMessage(ctx, v)
- //if err != nil {
- // log.Error("internal", "GetMessage err:", err, "ClientMsgID", v)
- // continue
- //}
- //attachInfo := sdk_struct.AttachedInfoElem{}
- //_ = utils.JsonStringToStruct(m.AttachedInfo, &attachInfo)
- //attachInfo.HasReadTime = rd.SendTime
- //m.AttachedInfo = utils.StructToJsonString(attachInfo)
- //m.IsRead = true
- //err = c.db.UpdateMessage(ctx, m)
- //if err != nil {
- // log.Error("internal", "setMessageHasReadByMsgID err:", err, "ClientMsgID", v)
- // continue
- //}
-
- msgIdListStatusOK = append(msgIdListStatusOK, v)
- }
- if len(msgIdListStatusOK) > 0 {
- msgRt := new(sdk_struct.MessageReceipt)
- msgRt.ContentType = rd.ContentType
- msgRt.MsgFrom = rd.MsgFrom
- msgRt.ReadTime = rd.SendTime
- msgRt.UserID = rd.SendID
- msgRt.SessionType = constant.SingleChatType
- msgRt.MsgIDList = msgIdListStatusOK
- messageReceiptResp = append(messageReceiptResp, msgRt)
- }
- if rd.SendID == c.loginUserID {
- conversationID = c.getConversationIDBySessionType(rd.RecvID, constant.SingleChatType)
- } else {
- conversationID = c.getConversationIDBySessionType(rd.SendID, constant.SingleChatType)
+ log.ZWarn(ctx, "GetSelfUserInfo err", err)
}
- if v, ok := chrsList[conversationID]; ok {
- chrsList[conversationID] = append(v, msgIdListStatusOK...)
- } else {
- chrsList[conversationID] = msgIdListStatusOK
+
+ if u.GlobalRecvMsgOpt != constant.ReceiveMessage {
+ return
}
- c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConversationLatestMsgHasRead, Args: chrsList}})
- }
- if len(messageReceiptResp) > 0 {
- // log.Info("internal", "OnRecvC2CReadReceipt: ", utils.StructToJsonString(messageReceiptResp))
- c.msgListener().OnRecvC2CReadReceipt(utils.StructToJsonString(messageReceiptResp))
- }
-}
+ for _, w := range newMessagesList {
+ conversationID := utils.GetConversationIDByMsg(w)
+ if v, ok := conversationChanged[conversationID]; ok && v.RecvMsgOpt == constant.ReceiveMessage {
+ needNotificationMsgList = append(needNotificationMsgList, w)
+ }
+ if v, ok := newConversation[conversationID]; ok && v.RecvMsgOpt == constant.ReceiveMessage {
+ needNotificationMsgList = append(needNotificationMsgList, w)
+ }
+ }
-type messageKvList struct {
- ClientMsgID string `json:"clientMsgID"`
- ChangedKvList []*sdk.SingleTypeKeyInfoSum `json:"changedKvList"`
-}
+ if len(needNotificationMsgList) != 0 {
+ c.msgListener().OnRecvOfflineNewMessage(utils.StructToJsonString(needNotificationMsgList))
+ }
+ } else { // online
+ for _, w := range newMessagesList {
+ if w.ContentType == constant.Typing {
+ continue
+ }
-func (c *Conversation) msgConvert(msg *sdk_struct.MsgStruct) (err error) {
- err = c.msgHandleByContentType(msg)
- if err != nil {
- return err
- } else {
- if msg.SessionType == constant.GroupChatType {
- msg.GroupID = msg.RecvID
- msg.RecvID = c.loginUserID
+ needNotificationMsgList = append(needNotificationMsgList, w)
}
- return nil
- }
-}
-func (c *Conversation) msgHandleByContentType(msg *sdk_struct.MsgStruct) (err error) {
- switch msg.ContentType {
- case constant.Text:
- t := sdk_struct.TextElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.TextElem = &t
- case constant.Picture:
- t := sdk_struct.PictureElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.PictureElem = &t
- case constant.Sound:
- t := sdk_struct.SoundElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.SoundElem = &t
- case constant.Video:
- t := sdk_struct.VideoElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.VideoElem = &t
- case constant.File:
- t := sdk_struct.FileElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.FileElem = &t
- case constant.AdvancedText:
- t := sdk_struct.AdvancedTextElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.AdvancedTextElem = &t
- case constant.AtText:
- t := sdk_struct.AtTextElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.AtTextElem = &t
- if err == nil {
- if utils.IsContain(c.loginUserID, msg.AtTextElem.AtUserList) {
- msg.AtTextElem.IsAtSelf = true
- }
+ if len(needNotificationMsgList) != 0 {
+ c.msgListener().OnRecvOnlineOnlyMessage(utils.StructToJsonString(needNotificationMsgList))
}
- case constant.Location:
- t := sdk_struct.LocationElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.LocationElem = &t
- case constant.Custom:
- fallthrough
- case constant.CustomMsgNotTriggerConversation:
- fallthrough
- case constant.CustomMsgOnlineOnly:
- t := sdk_struct.CustomElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.CustomElem = &t
- case constant.Typing:
- t := sdk_struct.TypingElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.TypingElem = &t
- case constant.Quote:
- t := sdk_struct.QuoteElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.QuoteElem = &t
- case constant.Merger:
- t := sdk_struct.MergeElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.MergeElem = &t
- case constant.Face:
- t := sdk_struct.FaceElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.FaceElem = &t
- case constant.Card:
- t := sdk_struct.CardElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.CardElem = &t
- default:
- t := sdk_struct.NotificationElem{}
- err = utils.JsonStringToStruct(msg.Content, &t)
- msg.NotificationElem = &t
}
- msg.Content = ""
-
- return utils.Wrap(err, "")
}
+
func (c *Conversation) updateConversation(lc *model_struct.LocalConversation, cs map[string]*model_struct.LocalConversation) {
if oldC, ok := cs[lc.ConversationID]; !ok {
cs[lc.ConversationID] = lc
@@ -884,74 +787,14 @@ func (c *Conversation) updateConversation(lc *model_struct.LocalConversation, cs
cs[lc.ConversationID] = oldC
}
}
- //if oldC, ok := cc[lc.ConversationID]; !ok {
- // oc, err := c.db.GetConversation(lc.ConversationID)
- // if err == nil && oc.ConversationID != "" {//如果会话已经存在
- // if lc.LatestMsgSendTime > oc.LatestMsgSendTime {
- // oc.UnreadCount = oc.UnreadCount + lc.UnreadCount
- // oc.LatestMsg = lc.LatestMsg
- // oc.LatestMsgSendTime = lc.LatestMsgSendTime
- // cc[lc.ConversationID] = *oc
- // } else {
- // oc.UnreadCount = oc.UnreadCount + lc.UnreadCount
- // cc[lc.ConversationID] = *oc
- // }
- // } else {
- // if oldC, ok := nc[lc.ConversationID]; !ok {
- // c.addFaceURLAndName(lc)
- // nc[lc.ConversationID] = *lc
- // } else {
- // if lc.LatestMsgSendTime > oldC.LatestMsgSendTime {
- // oldC.UnreadCount = oldC.UnreadCount + lc.UnreadCount
- // oldC.LatestMsg = lc.LatestMsg
- // oldC.LatestMsgSendTime = lc.LatestMsgSendTime
- // nc[lc.ConversationID] = oldC
- // } else {
- // oldC.UnreadCount = oldC.UnreadCount + lc.UnreadCount
- // nc[lc.ConversationID] = oldC
- // }
- // }
- // }
- //} else {
- // if lc.LatestMsgSendTime > oldC.LatestMsgSendTime {
- // oldC.UnreadCount = oldC.UnreadCount + lc.UnreadCount
- // oldC.LatestMsg = lc.LatestMsg
- // oldC.LatestMsgSendTime = lc.LatestMsgSendTime
- // cc[lc.ConversationID] = oldC
- // } else {
- // oldC.UnreadCount = oldC.UnreadCount + lc.UnreadCount
- // cc[lc.ConversationID] = oldC
- // }
- //
- //}
-
}
+
func mapConversationToList(m map[string]*model_struct.LocalConversation) (cs []*model_struct.LocalConversation) {
for _, v := range m {
cs = append(cs, v)
}
return cs
}
-func (c *Conversation) addFaceURLAndName(ctx context.Context, lc *model_struct.LocalConversation) error {
- switch lc.ConversationType {
- case constant.SingleChatType, constant.NotificationChatType:
- faceUrl, name, err := c.getUserNameAndFaceURL(ctx, lc.UserID)
- if err != nil {
- return err
- }
- lc.FaceURL = faceUrl
- lc.ShowName = name
-
- case constant.GroupChatType, constant.SuperGroupChatType:
- g, err := c.full.GetGroupInfoFromLocal2Svr(ctx, lc.GroupID, lc.ConversationType)
- if err != nil {
- return err
- }
- lc.ShowName = g.GroupName
- lc.FaceURL = g.FaceURL
- }
- return nil
-}
func (c *Conversation) batchAddFaceURLAndName(ctx context.Context, conversations ...*model_struct.LocalConversation) error {
if len(conversations) == 0 {
@@ -962,7 +805,7 @@ func (c *Conversation) batchAddFaceURLAndName(ctx context.Context, conversations
if conversation.ConversationType == constant.SingleChatType ||
conversation.ConversationType == constant.NotificationChatType {
userIDs = append(userIDs, conversation.UserID)
- } else if conversation.ConversationType == constant.SuperGroupChatType {
+ } else if conversation.ConversationType == constant.ReadGroupChatType {
groupIDs = append(groupIDs, conversation.GroupID)
}
}
@@ -973,10 +816,15 @@ func (c *Conversation) batchAddFaceURLAndName(ctx context.Context, conversations
return err
}
- groups, err := c.full.GetGroupsInfo(ctx, groupIDs...)
+ groupInfoList, err := c.group.GetSpecifiedGroupsInfo(ctx, groupIDs)
if err != nil {
return err
}
+
+ groups := datautil.SliceToMap(groupInfoList, func(groupInfo *model_struct.LocalGroup) string {
+ return groupInfo.GroupID
+ })
+
for _, conversation := range conversations {
if conversation.ConversationType == constant.SingleChatType ||
conversation.ConversationType == constant.NotificationChatType {
@@ -984,10 +832,12 @@ func (c *Conversation) batchAddFaceURLAndName(ctx context.Context, conversations
conversation.FaceURL = v.FaceURL
conversation.ShowName = v.Nickname
} else {
- log.ZWarn(ctx, "user info not found", errors.New("user not found"),
- "userID", conversation.UserID)
+ log.ZWarn(ctx, "user info not found", errors.New("user not found"), "userID", conversation.UserID)
+
+ conversation.FaceURL = ""
+ conversation.ShowName = "UserNotFound"
}
- } else if conversation.ConversationType == constant.SuperGroupChatType {
+ } else if conversation.ConversationType == constant.ReadGroupChatType {
if v, ok := groups[conversation.GroupID]; ok {
conversation.FaceURL = v.FaceURL
conversation.ShowName = v.GroupName
@@ -998,19 +848,20 @@ func (c *Conversation) batchAddFaceURLAndName(ctx context.Context, conversations
}
}
+
return nil
}
-func (c *Conversation) batchGetUserNameAndFaceURL(ctx context.Context, userIDs ...string) (map[string]*user.BasicInfo,
+
+func (c *Conversation) batchGetUserNameAndFaceURL(ctx context.Context, userIDs ...string) (map[string]*model_struct.LocalUser,
error) {
- m := make(map[string]*user.BasicInfo)
- var notCachedUserIDs []string
+ m := make(map[string]*model_struct.LocalUser)
var notInFriend []string
if len(userIDs) == 0 {
return m, nil
}
- friendList, err := c.friend.Db().GetFriendInfoList(ctx, userIDs)
+ friendList, err := c.relation.Db().GetFriendInfoList(ctx, userIDs)
if err != nil {
log.ZWarn(ctx, "BatchGetUserNameAndFaceURL", err, "userIDs", userIDs)
notInFriend = userIDs
@@ -1020,7 +871,7 @@ func (c *Conversation) batchGetUserNameAndFaceURL(ctx context.Context, userIDs .
}))
}
for _, localFriend := range friendList {
- userInfo := &user.BasicInfo{FaceURL: localFriend.FaceURL}
+ userInfo := &model_struct.LocalUser{UserID: localFriend.FriendUserID, FaceURL: localFriend.FaceURL}
if localFriend.Remark != "" {
userInfo.Nickname = localFriend.Remark
} else {
@@ -1029,34 +880,19 @@ func (c *Conversation) batchGetUserNameAndFaceURL(ctx context.Context, userIDs .
m[localFriend.FriendUserID] = userInfo
}
- for _, userID := range notInFriend {
- if value, ok := c.user.UserBasicCache.Load(userID); ok {
- m[userID] = value
- } else {
- notCachedUserIDs = append(notCachedUserIDs, userID)
- }
+ usersInfo, err := c.user.GetUsersInfoWithCache(ctx, notInFriend)
+ if err != nil {
+ return nil, err
}
- if len(notCachedUserIDs) > 0 {
- users, err := c.user.GetServerUserInfo(ctx, notCachedUserIDs)
- if err != nil {
- return nil, err
- }
- for _, u := range users {
- userInfo := &user.BasicInfo{FaceURL: u.FaceURL, Nickname: u.Nickname}
- m[u.UserID] = userInfo
- c.user.UserBasicCache.Store(u.UserID, userInfo)
- }
+ for _, userInfo := range usersInfo {
+ m[userInfo.UserID] = userInfo
}
return m, nil
}
+
func (c *Conversation) getUserNameAndFaceURL(ctx context.Context, userID string) (faceURL, name string, err error) {
- //find in cache
- if value, ok := c.user.UserBasicCache.Load(userID); ok {
- return value.FaceURL, value.Nickname, nil
- }
- //get from local db
- friendInfo, err := c.friend.Db().GetFriendInfoByFriendUserID(ctx, userID)
+ friendInfo, err := c.relation.Db().GetFriendInfoByFriendUserID(ctx, userID)
if err == nil {
faceURL = friendInfo.FaceURL
if friendInfo.Remark != "" {
@@ -1066,22 +902,9 @@ func (c *Conversation) getUserNameAndFaceURL(ctx context.Context, userID string)
}
return faceURL, name, nil
}
- //get from server db
- users, err := c.user.GetServerUserInfo(ctx, []string{userID})
+ userInfo, err := c.user.GetUserInfoWithCache(ctx, userID)
if err != nil {
- return "", "", err
+ return "", "", nil
}
- if len(users) == 0 {
- return "", "", sdkerrs.ErrUserIDNotFound.WrapMsg(userID)
- }
- c.user.UserBasicCache.Store(userID, &user.BasicInfo{FaceURL: users[0].FaceURL, Nickname: users[0].Nickname})
- return users[0].FaceURL, users[0].Nickname, nil
-}
-
-func (c *Conversation) GetInputStates(ctx context.Context, conversationID string, userID string) ([]int32, error) {
- return c.typing.GetInputStates(conversationID, userID), nil
-}
-
-func (c *Conversation) ChangeInputStates(ctx context.Context, conversationID string, focus bool) error {
- return c.typing.ChangeInputStates(ctx, conversationID, focus)
+ return userInfo.FaceURL, userInfo.Nickname, nil
}
diff --git a/internal/conversation_msg/conversation_notification.go b/internal/conversation_msg/conversation_notification.go
deleted file mode 100644
index 23830f118..000000000
--- a/internal/conversation_msg/conversation_notification.go
+++ /dev/null
@@ -1,777 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package conversation_msg
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "reflect"
- "runtime"
- "sync"
- "time"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/utils/datautil"
-
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-const (
- syncWait = iota
- asyncNoWait
- asyncWait
-)
-
-func (c *Conversation) Work(c2v common.Cmd2Value) {
- log.ZDebug(c2v.Ctx, "NotificationCmd start", "cmd", c2v.Cmd, "value", c2v.Value)
- defer log.ZDebug(c2v.Ctx, "NotificationCmd end", "cmd", c2v.Cmd, "value", c2v.Value)
- switch c2v.Cmd {
- case constant.CmdNewMsgCome:
- c.doMsgNew(c2v)
- case constant.CmdUpdateConversation:
- c.doUpdateConversation(c2v)
- case constant.CmdUpdateMessage:
- c.doUpdateMessage(c2v)
- case constant.CmSyncReactionExtensions:
- case constant.CmdNotification:
- c.doNotificationNew(c2v)
- case constant.CmdSyncData:
- c.syncData(c2v)
- }
-}
-
-func (c *Conversation) doNotificationNew(c2v common.Cmd2Value) {
- ctx := c2v.Ctx
- allMsg := c2v.Value.(sdk_struct.CmdNewMsgComeToConversation).Msgs
- syncFlag := c2v.Value.(sdk_struct.CmdNewMsgComeToConversation).SyncFlag
- switch syncFlag {
- case constant.AppDataSyncStart:
- log.ZDebug(ctx, "AppDataSyncStart")
- c.startTime = time.Now()
- c.ConversationListener().OnSyncServerStart(true)
- asyncWaitFunctions := []func(c context.Context) error{
- c.group.SyncAllJoinedGroupsAndMembers,
- c.friend.IncrSyncFriends,
- c.IncrSyncConversations,
- }
- runSyncFunctions(ctx, asyncWaitFunctions, asyncWait, c.ConversationListener().OnSyncServerProgress)
-
- syncWaitFunctions := []func(c context.Context) error{
- c.SyncAllConversationHashReadSeqs,
- }
- runSyncFunctions(ctx, syncWaitFunctions, syncWait, c.ConversationListener().OnSyncServerProgress)
- log.ZDebug(ctx, "core data sync over", "cost time", time.Since(c.startTime).Seconds())
-
- asyncNoWaitFunctions := []func(c context.Context) error{
- c.user.SyncLoginUserInfoWithoutNotice,
- c.friend.SyncAllBlackListWithoutNotice,
- c.friend.SyncAllFriendApplicationWithoutNotice,
- c.friend.SyncAllSelfFriendApplicationWithoutNotice,
- c.group.SyncAllAdminGroupApplicationWithoutNotice,
- c.group.SyncAllSelfGroupApplicationWithoutNotice,
- c.user.SyncAllCommandWithoutNotice,
- }
- runSyncFunctions(ctx, asyncNoWaitFunctions, asyncNoWait, c.ConversationListener().OnSyncServerProgress)
-
- case constant.AppDataSyncFinish:
- log.ZDebug(ctx, "AppDataSyncFinish", "time", time.Since(c.startTime).Milliseconds())
- c.ConversationListener().OnSyncServerFinish(true)
- case constant.MsgSyncBegin:
- log.ZDebug(ctx, "MsgSyncBegin")
- c.ConversationListener().OnSyncServerStart(false)
-
- c.syncData(c2v)
-
- case constant.MsgSyncFailed:
- c.ConversationListener().OnSyncServerFailed(false)
- case constant.MsgSyncEnd:
- log.ZDebug(ctx, "MsgSyncEnd", "time", time.Since(c.startTime).Milliseconds())
- c.ConversationListener().OnSyncServerFinish(false)
- }
-
- for conversationID, msgs := range allMsg {
- log.ZDebug(ctx, "notification handling", "conversationID", conversationID, "msgs", msgs)
- if len(msgs.Msgs) != 0 {
- lastMsg := msgs.Msgs[len(msgs.Msgs)-1]
- log.ZDebug(ctx, "SetNotificationSeq", "conversationID", conversationID, "seq", lastMsg.Seq)
- if lastMsg.Seq != 0 {
- if err := c.db.SetNotificationSeq(ctx, conversationID, lastMsg.Seq); err != nil {
- log.ZError(ctx, "SetNotificationSeq err", err, "conversationID", conversationID, "lastMsg", lastMsg)
- }
- }
- }
- for _, v := range msgs.Msgs {
- switch {
- case v.ContentType == constant.ConversationChangeNotification:
- c.DoConversationChangedNotification(ctx, v)
- case v.ContentType == constant.ConversationPrivateChatNotification:
- c.DoConversationIsPrivateChangedNotification(ctx, v)
- case v.ContentType == constant.ConversationUnreadNotification:
- var tips sdkws.ConversationHasReadTips
- _ = json.Unmarshal(v.Content, &tips)
- c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: tips.ConversationID, Action: constant.UnreadCountSetZero}})
- c.db.DeleteConversationUnreadMessageList(ctx, tips.ConversationID, tips.UnreadCountTime)
- c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{tips.ConversationID}}})
- continue
- case v.ContentType == constant.BusinessNotification:
- c.business.DoNotification(ctx, v)
- continue
- case v.ContentType == constant.RevokeNotification:
- c.doRevokeMsg(ctx, v)
- case v.ContentType == constant.ClearConversationNotification:
- c.doClearConversations(ctx, v)
- case v.ContentType == constant.DeleteMsgsNotification:
- c.doDeleteMsgs(ctx, v)
- case v.ContentType == constant.HasReadReceipt:
- c.doReadDrawing(ctx, v)
- }
-
- switch v.SessionType {
- case constant.SingleChatType:
- if v.ContentType > constant.FriendNotificationBegin && v.ContentType < constant.FriendNotificationEnd {
- c.friend.DoNotification(ctx, v)
- } else if v.ContentType > constant.UserNotificationBegin && v.ContentType < constant.UserNotificationEnd {
- c.user.DoNotification(ctx, v)
- } else if datautil.Contain(v.ContentType, constant.GroupApplicationRejectedNotification, constant.GroupApplicationAcceptedNotification, constant.JoinGroupApplicationNotification) {
- c.group.DoNotification(ctx, v)
- } else if v.ContentType > constant.SignalingNotificationBegin && v.ContentType < constant.SignalingNotificationEnd {
-
- continue
- }
- case constant.GroupChatType, constant.SuperGroupChatType:
- if v.ContentType > constant.GroupNotificationBegin && v.ContentType < constant.GroupNotificationEnd {
- c.group.DoNotification(ctx, v)
- } else if v.ContentType > constant.SignalingNotificationBegin && v.ContentType < constant.SignalingNotificationEnd {
- continue
- }
- }
- }
- }
-
-}
-
-func (c *Conversation) getConversationLatestMsgClientID(latestMsg string) string {
- msg := &sdk_struct.MsgStruct{}
- if err := json.Unmarshal([]byte(latestMsg), msg); err != nil {
- log.ZError(context.Background(), "getConversationLatestMsgClientID", err, "latestMsg", latestMsg)
- }
- return msg.ClientMsgID
-}
-
-func (c *Conversation) doUpdateConversation(c2v common.Cmd2Value) {
- ctx := c2v.Ctx
- node := c2v.Value.(common.UpdateConNode)
- switch node.Action {
- case constant.AddConOrUpLatMsg:
- var list []*model_struct.LocalConversation
- lc := node.Args.(model_struct.LocalConversation)
- oc, err := c.db.GetConversation(ctx, lc.ConversationID)
- if err == nil {
- // log.Info("this is old conversation", *oc)
- if lc.LatestMsgSendTime >= oc.LatestMsgSendTime || c.getConversationLatestMsgClientID(lc.LatestMsg) == c.getConversationLatestMsgClientID(oc.LatestMsg) { // The session update of asynchronous messages is subject to the latest sending time
- err := c.db.UpdateColumnsConversation(ctx, node.ConID, map[string]interface{}{"latest_msg_send_time": lc.LatestMsgSendTime, "latest_msg": lc.LatestMsg})
- if err != nil {
- log.ZError(ctx, "updateConversationLatestMsgModel", err, "conversationID", node.ConID)
- } else {
- oc.LatestMsgSendTime = lc.LatestMsgSendTime
- oc.LatestMsg = lc.LatestMsg
- list = append(list, oc)
- c.ConversationListener().OnConversationChanged(utils.StructToJsonString(list))
- }
- }
- } else {
- // log.Info("this is new conversation", lc)
- err4 := c.db.InsertConversation(ctx, &lc)
- if err4 != nil {
- // log.Error("internal", "insert new conversation err:", err4.Error())
- } else {
- list = append(list, &lc)
- c.ConversationListener().OnNewConversation(utils.StructToJsonString(list))
- }
- }
-
- case constant.UnreadCountSetZero:
- if err := c.db.UpdateColumnsConversation(ctx, node.ConID, map[string]interface{}{"unread_count": 0}); err != nil {
- log.ZError(ctx, "updateConversationUnreadCountModel err", err, "conversationID", node.ConID)
- } else {
- totalUnreadCount, err := c.db.GetTotalUnreadMsgCountDB(ctx)
- if err == nil {
- c.ConversationListener().OnTotalUnreadMessageCountChanged(totalUnreadCount)
- } else {
- log.ZError(ctx, "getTotalUnreadMsgCountDB err", err)
- }
-
- }
- case constant.IncrUnread:
- err := c.db.IncrConversationUnreadCount(ctx, node.ConID)
- if err != nil {
- // log.Error("internal", "incrConversationUnreadCount database err:", err.Error())
- return
- }
- case constant.TotalUnreadMessageChanged:
- totalUnreadCount, err := c.db.GetTotalUnreadMsgCountDB(ctx)
- if err != nil {
- // log.Error("internal", "TotalUnreadMessageChanged database err:", err.Error())
- } else {
- c.ConversationListener().OnTotalUnreadMessageCountChanged(totalUnreadCount)
- }
- case constant.UpdateConFaceUrlAndNickName:
- var lc model_struct.LocalConversation
- st := node.Args.(common.SourceIDAndSessionType)
- log.ZInfo(ctx, "UpdateConFaceUrlAndNickName", "st", st)
- switch st.SessionType {
- case constant.SingleChatType:
- lc.UserID = st.SourceID
- lc.ConversationID = c.getConversationIDBySessionType(st.SourceID, constant.SingleChatType)
- lc.ConversationType = constant.SingleChatType
- case constant.SuperGroupChatType:
- conversationID, conversationType, err := c.getConversationTypeByGroupID(ctx, st.SourceID)
- if err != nil {
- return
- }
- lc.GroupID = st.SourceID
- lc.ConversationID = conversationID
- lc.ConversationType = conversationType
- case constant.NotificationChatType:
- lc.UserID = st.SourceID
- lc.ConversationID = c.getConversationIDBySessionType(st.SourceID, constant.NotificationChatType)
- lc.ConversationType = constant.NotificationChatType
- default:
- log.ZError(ctx, "not support sessionType", nil, "sessionType", st.SessionType)
- return
- }
- lc.ShowName = st.Nickname
- lc.FaceURL = st.FaceURL
- err := c.db.UpdateConversation(ctx, &lc)
- if err != nil {
- // log.Error("internal", "setConversationFaceUrlAndNickName database err:", err.Error())
- return
- }
- c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: lc.ConversationID, Action: constant.ConChange, Args: []string{lc.ConversationID}}})
-
- case constant.UpdateLatestMessageChange:
- conversationID := node.ConID
- var latestMsg sdk_struct.MsgStruct
- l, err := c.db.GetConversation(ctx, conversationID)
- if err != nil {
- log.ZError(ctx, "getConversationLatestMsgModel err", err, "conversationID", conversationID)
- } else {
- err := json.Unmarshal([]byte(l.LatestMsg), &latestMsg)
- if err != nil {
- log.ZError(ctx, "latestMsg,Unmarshal err", err)
- } else {
- latestMsg.IsRead = true
- newLatestMessage := utils.StructToJsonString(latestMsg)
- err = c.db.UpdateColumnsConversation(ctx, node.ConID, map[string]interface{}{"latest_msg_send_time": latestMsg.SendTime, "latest_msg": newLatestMessage})
- if err != nil {
- log.ZError(ctx, "updateConversationLatestMsgModel err", err)
- }
- }
- }
- case constant.ConChange:
- conversationIDs := node.Args.([]string)
- conversations, err := c.db.GetMultipleConversationDB(ctx, conversationIDs)
- if err != nil {
- log.ZError(ctx, "getMultipleConversationModel err", err)
- } else {
- var newCList []*model_struct.LocalConversation
- for _, v := range conversations {
- if v.LatestMsgSendTime != 0 {
- newCList = append(newCList, v)
- }
- }
- c.ConversationListener().OnConversationChanged(utils.StructToJsonStringDefault(newCList))
- }
- case constant.NewCon:
- cidList := node.Args.([]string)
- cLists, err := c.db.GetMultipleConversationDB(ctx, cidList)
- if err != nil {
- // log.Error("internal", "getMultipleConversationModel err :", err.Error())
- } else {
- if cLists != nil {
- // log.Info("internal", "getMultipleConversationModel success :", cLists)
- c.ConversationListener().OnNewConversation(utils.StructToJsonString(cLists))
- }
- }
- case constant.ConChangeDirect:
- cidList := node.Args.(string)
- c.ConversationListener().OnConversationChanged(cidList)
-
- case constant.NewConDirect:
- cidList := node.Args.(string)
- // log.Debug("internal", "NewConversation", cidList)
- c.ConversationListener().OnNewConversation(cidList)
-
- case constant.ConversationLatestMsgHasRead:
- hasReadMsgList := node.Args.(map[string][]string)
- var result []*model_struct.LocalConversation
- var latestMsg sdk_struct.MsgStruct
- var lc model_struct.LocalConversation
- for conversationID, msgIDList := range hasReadMsgList {
- LocalConversation, err := c.db.GetConversation(ctx, conversationID)
- if err != nil {
- // log.Error("internal", "get conversation err", err.Error(), conversationID)
- continue
- }
- err = utils.JsonStringToStruct(LocalConversation.LatestMsg, &latestMsg)
- if err != nil {
- // log.Error("internal", "JsonStringToStruct err", err.Error(), conversationID)
- continue
- }
- if utils.IsContain(latestMsg.ClientMsgID, msgIDList) {
- latestMsg.IsRead = true
- lc.ConversationID = conversationID
- lc.LatestMsg = utils.StructToJsonString(latestMsg)
- LocalConversation.LatestMsg = utils.StructToJsonString(latestMsg)
- err := c.db.UpdateConversation(ctx, &lc)
- if err != nil {
- // log.Error("internal", "UpdateConversation database err:", err.Error())
- continue
- } else {
- result = append(result, LocalConversation)
- }
- }
- }
- if result != nil {
- // log.Info("internal", "getMultipleConversationModel success :", result)
- c.ConversationListener().OnNewConversation(utils.StructToJsonString(result))
- }
- case constant.SyncConversation:
-
- }
-}
-
-func (c *Conversation) syncData(c2v common.Cmd2Value) {
- ctx := c2v.Ctx
- c.startTime = time.Now()
- //clear SubscriptionStatusMap
- c.user.OnlineStatusCache.DeleteAll()
-
- // Synchronous sync functions
- syncFuncs := []func(c context.Context) error{
- c.SyncAllConversationHashReadSeqs,
- }
-
- runSyncFunctions(ctx, syncFuncs, syncWait, nil)
-
- // Asynchronous sync functions
- asyncFuncs := []func(c context.Context) error{
- c.user.SyncLoginUserInfo,
- c.friend.SyncAllBlackList,
- c.friend.SyncAllFriendApplication,
- c.friend.SyncAllSelfFriendApplication,
- c.group.SyncAllAdminGroupApplication,
- c.group.SyncAllSelfGroupApplication,
- c.user.SyncAllCommand,
- c.group.SyncAllJoinedGroupsAndMembers,
- c.friend.IncrSyncFriends,
- c.IncrSyncConversations,
- }
-
- runSyncFunctions(ctx, asyncFuncs, asyncNoWait, nil)
-}
-
-func runSyncFunctions(ctx context.Context, funcs []func(c context.Context) error, mode int, progressCallback func(progress int)) {
- totalFuncs := len(funcs)
- var wg sync.WaitGroup
-
- for i, fn := range funcs {
- switch mode {
- case asyncWait:
- wg.Add(1)
- go executeSyncFunction(ctx, fn, i, totalFuncs, progressCallback, &wg)
- case asyncNoWait:
- go executeSyncFunction(ctx, fn, i, totalFuncs, progressCallback, nil)
- case syncWait:
- executeSyncFunction(ctx, fn, i, totalFuncs, progressCallback, nil)
- }
- }
-
- if mode == asyncWait {
- wg.Wait()
- }
-}
-
-func executeSyncFunction(ctx context.Context, fn func(c context.Context) error, index, total int, progressCallback func(progress int), wg *sync.WaitGroup) {
- if wg != nil {
- defer wg.Done()
- }
-
- funcName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
- startTime := time.Now()
- err := fn(ctx)
- duration := time.Since(startTime)
- if err != nil {
- log.ZWarn(ctx, fmt.Sprintf("%s sync error", funcName), err, "duration", duration.Seconds())
- } else {
- log.ZDebug(ctx, fmt.Sprintf("%s completed successfully", funcName), "duration", duration.Seconds())
- }
- if progressCallback != nil {
- progress := int(float64(index+1) / float64(total) * 100)
- if progress == 0 {
- progress = 1
- }
- progressCallback(progress)
- }
-}
-
-func (c *Conversation) doUpdateMessage(c2v common.Cmd2Value) {
- node := c2v.Value.(common.UpdateMessageNode)
- ctx := c2v.Ctx
- switch node.Action {
- case constant.UpdateMsgFaceUrlAndNickName:
- args := node.Args.(common.UpdateMessageInfo)
- switch args.SessionType {
- case constant.SingleChatType:
- if args.UserID == c.loginUserID {
- conversationIDList, err := c.db.GetAllSingleConversationIDList(ctx)
- if err != nil {
- log.ZError(ctx, "GetAllSingleConversationIDList err", err)
- return
- } else {
- log.ZDebug(ctx, "get single conversationID list", "conversationIDList", conversationIDList)
- for _, conversationID := range conversationIDList {
- err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
- if err != nil {
- log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
- continue
- }
- }
-
- }
- } else {
- conversationID := c.getConversationIDBySessionType(args.UserID, constant.SingleChatType)
- err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
- if err != nil {
- log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
- }
-
- }
- case constant.SuperGroupChatType:
- conversationID := c.getConversationIDBySessionType(args.GroupID, constant.SuperGroupChatType)
- err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
- if err != nil {
- log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
- }
- case constant.NotificationChatType:
- conversationID := c.getConversationIDBySessionType(args.UserID, constant.NotificationChatType)
- err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
- if err != nil {
- log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
- }
- default:
- log.ZError(ctx, "not support sessionType", nil, "args", args)
- return
- }
- }
-
-}
-
-// funcation (c *Conversation) doSyncReactionExtensions(c2v common.Cmd2Value) {
-// if c.ConversationListener == nil {
-// // log.Error("internal", "not set conversationListener")
-// return
-// }
-// node := c2v.Value.(common.SyncReactionExtensionsNode)
-// ctx := mcontext.NewCtx(node.OperationID)
-// switch node.Action {
-// case constant.SyncMessageListReactionExtensions:
-// args := node.Args.(syncReactionExtensionParams)
-// // log.Error(node.OperationID, "come SyncMessageListReactionExtensions", args)
-// var reqList []server_api_params.OperateMessageListReactionExtensionsReq
-// for _, v := range args.MessageList {
-// var temp server_api_params.OperateMessageListReactionExtensionsReq
-// temp.ClientMsgID = v.ClientMsgID
-// temp.MsgFirstModifyTime = v.MsgFirstModifyTime
-// reqList = append(reqList, temp)
-// }
-// var apiReq server_api_params.GetMessageListReactionExtensionsReq
-// apiReq.SourceID = args.SourceID
-// apiReq.TypeKeyList = args.TypeKeyList
-// apiReq.SessionType = args.SessionType
-// apiReq.MessageReactionKeyList = reqList
-// apiReq.IsExternalExtensions = args.IsExternalExtension
-// apiReq.OperationID = node.OperationID
-// apiResp, err := util.CallApi[server_api_params.GetMessageListReactionExtensionsResp](ctx, constant.GetMessageListReactionExtensionsRouter, &apiReq)
-// if err != nil {
-// // log.NewError(node.OperationID, utils.GetSelfFuncName(), "getMessageListReactionExtensions err:", err.Error())
-// return
-// }
-// // for _, result := range apiResp {
-// // log.Warn(node.OperationID, "api return reslut is:", result.ClientMsgID, result.ReactionExtensionList)
-// // }
-// onLocal := funcation(data []*model_struct.LocalChatLogReactionExtensions) []*server_api_params.SingleMessageExtensionResult {
-// var result []*server_api_params.SingleMessageExtensionResult
-// for _, v := range data {
-// temp := new(server_api_params.SingleMessageExtensionResult)
-// tempMap := make(map[string]*sdkws.KeyValue)
-// _ = json.Unmarshal(v.LocalReactionExtensions, &tempMap)
-// if len(args.TypeKeyList) != 0 {
-// for s, _ := range tempMap {
-// if !utils.IsContain(s, args.TypeKeyList) {
-// delete(tempMap, s)
-// }
-// }
-// }
-//
-// temp.ReactionExtensionList = tempMap
-// temp.ClientMsgID = v.ClientMsgID
-// result = append(result, temp)
-// }
-// return result
-// }(args.ExtendMessageList)
-// var onServer []*server_api_params.SingleMessageExtensionResult
-// for _, v := range *apiResp {
-// if v.ErrCode == 0 {
-// onServer = append(onServer, v)
-// }
-// }
-// aInBNot, _, sameA, _ := common.CheckReactionExtensionsDiff(onServer, onLocal)
-// for _, v := range aInBNot {
-// // log.Error(node.OperationID, "come InsertMessageReactionExtension", args, v.ClientMsgID)
-// if len(v.ReactionExtensionList) > 0 {
-// temp := model_struct.LocalChatLogReactionExtensions{ClientMsgID: v.ClientMsgID, LocalReactionExtensions: []byte(utils.StructToJsonString(v.ReactionExtensionList))}
-// err := c.db.InsertMessageReactionExtension(ctx, &temp)
-// if err != nil {
-// // log.Error(node.OperationID, "InsertMessageReactionExtension err:", err.Error())
-// continue
-// }
-// }
-// var changedKv []*sdkws.KeyValue
-// for _, value := range v.ReactionExtensionList {
-// changedKv = append(changedKv, value)
-// }
-// if len(changedKv) > 0 {
-// c.msgListener.OnRecvMessageExtensionsChanged(v.ClientMsgID, utils.StructToJsonString(changedKv))
-// }
-// }
-// // for _, result := range sameA {
-// // log.ZWarn(ctx, "result", result.ReactionExtensionList, result.ClientMsgID)
-// // }
-// for _, v := range sameA {
-// // log.Error(node.OperationID, "come sameA", v.ClientMsgID, v.ReactionExtensionList)
-// tempMap := make(map[string]*sdkws.KeyValue)
-// for _, extensions := range args.ExtendMessageList {
-// if v.ClientMsgID == extensions.ClientMsgID {
-// _ = json.Unmarshal(extensions.LocalReactionExtensions, &tempMap)
-// break
-// }
-// }
-// if len(v.ReactionExtensionList) == 0 {
-// err := c.db.DeleteMessageReactionExtension(ctx, v.ClientMsgID)
-// if err != nil {
-// // log.Error(node.OperationID, "DeleteMessageReactionExtension err:", err.Error())
-// continue
-// }
-// var deleteKeyList []string
-// for key, _ := range tempMap {
-// deleteKeyList = append(deleteKeyList, key)
-// }
-// if len(deleteKeyList) > 0 {
-// c.msgListener.OnRecvMessageExtensionsDeleted(v.ClientMsgID, utils.StructToJsonString(deleteKeyList))
-// }
-// } else {
-// deleteKeyList, changedKv := funcation(local, server map[string]*sdkws.KeyValue) ([]string, []*sdkws.KeyValue) {
-// var deleteKeyList []string
-// var changedKv []*sdkws.KeyValue
-// for k, v := range local {
-// ia, ok := server[k]
-// if ok {
-// //服务器不同的kv
-// if ia.Value != v.Value {
-// changedKv = append(changedKv, ia)
-// }
-// } else {
-// //服务器已经没有kv
-// deleteKeyList = append(deleteKeyList, k)
-// }
-// }
-// //从服务器新增的kv
-// for k, v := range server {
-// _, ok := local[k]
-// if !ok {
-// changedKv = append(changedKv, v)
-//
-// }
-// }
-// return deleteKeyList, changedKv
-// }(tempMap, v.ReactionExtensionList)
-// extendMsg := model_struct.LocalChatLogReactionExtensions{ClientMsgID: v.ClientMsgID, LocalReactionExtensions: []byte(utils.StructToJsonString(v.ReactionExtensionList))}
-// err = c.db.UpdateMessageReactionExtension(ctx, &extendMsg)
-// if err != nil {
-// // log.Error(node.OperationID, "UpdateMessageReactionExtension err:", err.Error())
-// continue
-// }
-// if len(deleteKeyList) > 0 {
-// c.msgListener.OnRecvMessageExtensionsDeleted(v.ClientMsgID, utils.StructToJsonString(deleteKeyList))
-// }
-// if len(changedKv) > 0 {
-// c.msgListener.OnRecvMessageExtensionsChanged(v.ClientMsgID, utils.StructToJsonString(changedKv))
-// }
-// }
-// //err := c.db.GetAndUpdateMessageReactionExtension(v.ClientMsgID, v.ReactionExtensionList)
-// //if err != nil {
-// // log.Error(node.OperationID, "GetAndUpdateMessageReactionExtension err:", err.Error())
-// // continue
-// //}
-// //var changedKv []*server_api_params.KeyValue
-// //for _, value := range v.ReactionExtensionList {
-// // changedKv = append(changedKv, value)
-// //}
-// //if len(changedKv) > 0 {
-// // c.msgListener.OnRecvMessageExtensionsChanged(v.ClientMsgID, utils.StructToJsonString(changedKv))
-// //}
-// }
-// case constant.SyncMessageListTypeKeyInfo:
-// messageList := node.Args.([]*sdk_struct.MsgStruct)
-// var sourceID string
-// var sessionType int32
-// var reqList []server_api_params.OperateMessageListReactionExtensionsReq
-// var temp server_api_params.OperateMessageListReactionExtensionsReq
-// for _, v := range messageList {
-// //todo syncMessage must sync
-// message, err := c.db.GetMessage(ctx, "", v.ClientMsgID)
-// if err != nil {
-// // log.Error(node.OperationID, "GetMessageController err:", err.Error(), *v)
-// continue
-// }
-// temp.ClientMsgID = message.ClientMsgID
-// temp.MsgFirstModifyTime = message.MsgFirstModifyTime
-// reqList = append(reqList, temp)
-// switch message.SessionType {
-// case constant.SingleChatType:
-// sourceID = message.SendID + message.RecvID
-// case constant.NotificationChatType:
-// sourceID = message.RecvID
-// case constant.GroupChatType, constant.SuperGroupChatType:
-// sourceID = message.RecvID
-// }
-// sessionType = message.SessionType
-// }
-// var apiReq server_api_params.GetMessageListReactionExtensionsReq
-// apiReq.SourceID = sourceID
-// apiReq.SessionType = sessionType
-// apiReq.MessageReactionKeyList = reqList
-// apiReq.OperationID = node.OperationID
-// //var apiResp server_api_params.GetMessageListReactionExtensionsResp
-//
-// apiResp, err := util.CallApi[server_api_params.GetMessageListReactionExtensionsResp](ctx, constant.GetMessageListReactionExtensionsRouter, &apiReq)
-// if err != nil {
-// // log.Error(node.OperationID, "GetMessageListReactionExtensions from server err:", err.Error(), apiReq)
-// return
-// }
-// var messageChangedList []*messageKvList
-// for _, v := range *apiResp {
-// if v.ErrCode == 0 {
-// var changedKv []*sdkws.KeyValue
-// var prefixTypeKey []string
-// extendMsg, _ := c.db.GetMessageReactionExtension(ctx, v.ClientMsgID)
-// localKV := make(map[string]*sdkws.KeyValue)
-// _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &localKV)
-// for typeKey, value := range v.ReactionExtensionList {
-// oldValue, ok := localKV[typeKey]
-// if ok {
-// if !cmp.Equal(value, oldValue) {
-// localKV[typeKey] = value
-// prefixTypeKey = append(prefixTypeKey, getPrefixTypeKey(typeKey))
-// changedKv = append(changedKv, value)
-// }
-// } else {
-// localKV[typeKey] = value
-// prefixTypeKey = append(prefixTypeKey, getPrefixTypeKey(typeKey))
-// changedKv = append(changedKv, value)
-//
-// }
-//
-// }
-// extendMsg.LocalReactionExtensions = []byte(utils.StructToJsonString(localKV))
-// _ = c.db.UpdateMessageReactionExtension(ctx, extendMsg)
-// if len(changedKv) > 0 {
-// c.msgListener.OnRecvMessageExtensionsChanged(extendMsg.ClientMsgID, utils.StructToJsonString(changedKv))
-// }
-// prefixTypeKey = utils.RemoveRepeatedStringInList(prefixTypeKey)
-// if len(prefixTypeKey) > 0 && c.msgKvListener != nil {
-// var result []*sdk.SingleTypeKeyInfoSum
-// oneMessageChanged := new(messageKvList)
-// oneMessageChanged.ClientMsgID = extendMsg.ClientMsgID
-// for _, v := range prefixTypeKey {
-// singleResult := new(sdk.SingleTypeKeyInfoSum)
-// singleResult.TypeKey = v
-// for typeKey, value := range localKV {
-// if strings.HasPrefix(typeKey, v) {
-// singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
-// err := json.Unmarshal([]byte(value.Value), singleTypeKeyInfo)
-// if err != nil {
-// continue
-// }
-// if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
-// singleResult.IsContainSelf = true
-// }
-// for _, info := range singleTypeKeyInfo.InfoList {
-// v := *info
-// singleResult.InfoList = append(singleResult.InfoList, &v)
-// }
-// singleResult.Counter += singleTypeKeyInfo.Counter
-// }
-// }
-// result = append(result, singleResult)
-// }
-// oneMessageChanged.ChangedKvList = result
-// messageChangedList = append(messageChangedList, oneMessageChanged)
-// }
-// }
-// }
-// if len(messageChangedList) > 0 && c.msgKvListener != nil {
-// c.msgKvListener.OnMessageKvInfoChanged(utils.StructToJsonString(messageChangedList))
-// }
-//
-// }
-//
-// }
-
-func (c *Conversation) DoConversationChangedNotification(ctx context.Context, msg *sdkws.MsgData) {
- //var notification sdkws.ConversationChangedNotification
- tips := &sdkws.ConversationUpdateTips{}
- if err := utils.UnmarshalNotificationElem(msg.Content, tips); err != nil {
- log.ZError(ctx, "UnmarshalNotificationElem err", err, "msg", msg)
- return
- }
-
- err := c.IncrSyncConversations(ctx)
- if err != nil {
- log.ZWarn(ctx, "IncrSyncConversations err", err)
- }
-
-}
-
-func (c *Conversation) DoConversationIsPrivateChangedNotification(ctx context.Context, msg *sdkws.MsgData) {
- tips := &sdkws.ConversationSetPrivateTips{}
- if err := utils.UnmarshalNotificationElem(msg.Content, tips); err != nil {
- log.ZError(ctx, "UnmarshalNotificationElem err", err, "msg", msg)
- return
- }
-
- err := c.IncrSyncConversations(ctx)
- if err != nil {
- log.ZWarn(ctx, "IncrSyncConversations err", err)
- }
-
-}
diff --git a/internal/conversation_msg/conversion.go b/internal/conversation_msg/conversion.go
new file mode 100644
index 000000000..5ad908aea
--- /dev/null
+++ b/internal/conversation_msg/conversion.go
@@ -0,0 +1,275 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package conversation_msg
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ pconstant "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+
+ pbConversation "github.com/openimsdk/protocol/conversation"
+)
+
+func ServerConversationToLocal(conversation *pbConversation.Conversation) *model_struct.LocalConversation {
+ return &model_struct.LocalConversation{
+ ConversationID: conversation.ConversationID,
+ ConversationType: conversation.ConversationType,
+ UserID: conversation.UserID,
+ GroupID: conversation.GroupID,
+ RecvMsgOpt: conversation.RecvMsgOpt,
+ GroupAtType: conversation.GroupAtType,
+ IsPinned: conversation.IsPinned,
+ BurnDuration: conversation.BurnDuration,
+ IsPrivateChat: conversation.IsPrivateChat,
+ AttachedInfo: conversation.AttachedInfo,
+ Ex: conversation.Ex,
+ MsgDestructTime: conversation.MsgDestructTime,
+ IsMsgDestruct: conversation.IsMsgDestruct,
+ }
+}
+
+func LocalConversationToServer(conversation *model_struct.LocalConversation) *pbConversation.Conversation {
+ return &pbConversation.Conversation{
+ ConversationID: conversation.ConversationID,
+ ConversationType: conversation.ConversationType,
+ UserID: conversation.UserID,
+ GroupID: conversation.GroupID,
+ RecvMsgOpt: conversation.RecvMsgOpt,
+ GroupAtType: conversation.GroupAtType,
+ IsPinned: conversation.IsPinned,
+ BurnDuration: conversation.BurnDuration,
+ IsPrivateChat: conversation.IsPrivateChat,
+ AttachedInfo: conversation.AttachedInfo,
+ MsgDestructTime: conversation.MsgDestructTime,
+ Ex: conversation.Ex,
+ IsMsgDestruct: conversation.IsMsgDestruct,
+ }
+}
+
+func MsgDataToLocalChatLog(serverMessage *sdkws.MsgData) *model_struct.LocalChatLog {
+ localMessage := &model_struct.LocalChatLog{
+ ClientMsgID: serverMessage.ClientMsgID,
+ ServerMsgID: serverMessage.ServerMsgID,
+ SendID: serverMessage.SendID,
+ RecvID: serverMessage.RecvID,
+ SenderPlatformID: serverMessage.SenderPlatformID,
+ SenderNickname: serverMessage.SenderNickname,
+ SenderFaceURL: serverMessage.SenderFaceURL,
+ SessionType: serverMessage.SessionType,
+ MsgFrom: serverMessage.MsgFrom,
+ ContentType: serverMessage.ContentType,
+ Content: string(serverMessage.Content),
+ IsRead: serverMessage.IsRead,
+ Seq: serverMessage.Seq,
+ SendTime: serverMessage.SendTime,
+ CreateTime: serverMessage.CreateTime,
+ AttachedInfo: serverMessage.AttachedInfo,
+ Ex: serverMessage.Ex,
+ }
+
+ if serverMessage.Status >= constant.MsgStatusHasDeleted {
+ localMessage.Status = serverMessage.Status
+ } else {
+ localMessage.Status = constant.MsgStatusSendSuccess
+ }
+
+ if serverMessage.SessionType == constant.WriteGroupChatType || serverMessage.SessionType == constant.ReadGroupChatType {
+ localMessage.RecvID = serverMessage.GroupID
+ }
+ return localMessage
+}
+
+func LocalChatLogToMsgStruct(localMessage *model_struct.LocalChatLog) *sdk_struct.MsgStruct {
+ message := &sdk_struct.MsgStruct{
+ ClientMsgID: localMessage.ClientMsgID,
+ ServerMsgID: localMessage.ServerMsgID,
+ CreateTime: localMessage.CreateTime,
+ SendTime: localMessage.SendTime,
+ SessionType: localMessage.SessionType,
+ SendID: localMessage.SendID,
+ RecvID: localMessage.RecvID,
+ MsgFrom: localMessage.MsgFrom,
+ ContentType: localMessage.ContentType,
+ SenderPlatformID: localMessage.SenderPlatformID,
+ SenderNickname: localMessage.SenderNickname,
+ SenderFaceURL: localMessage.SenderFaceURL,
+ Content: localMessage.Content,
+ Seq: localMessage.Seq,
+ IsRead: localMessage.IsRead,
+ Status: localMessage.Status,
+ Ex: localMessage.Ex,
+ LocalEx: localMessage.LocalEx,
+ }
+ var attachedInfo sdk_struct.AttachedInfoElem
+ err := utils.JsonStringToStruct(localMessage.AttachedInfo, &attachedInfo)
+ if err != nil {
+ log.ZWarn(context.Background(), "JsonStringToStruct error", err, "localMessage.AttachedInfo", localMessage.AttachedInfo)
+ }
+ message.AttachedInfoElem = &attachedInfo
+ errParse := msgHandleByContentType(message)
+ if errParse != nil {
+ log.ZWarn(context.Background(), "Parsing data error", err, "messageContent", message.Content)
+ }
+ switch localMessage.SessionType {
+ case constant.WriteGroupChatType:
+ fallthrough
+ case constant.ReadGroupChatType:
+ message.GroupID = localMessage.RecvID
+ }
+ return message
+}
+
+func msgHandleByContentType(msg *sdk_struct.MsgStruct) (err error) {
+ switch msg.ContentType {
+ case constant.Text:
+ t := sdk_struct.TextElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.TextElem = &t
+ case constant.Picture:
+ t := sdk_struct.PictureElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.PictureElem = &t
+ case constant.Sound:
+ t := sdk_struct.SoundElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.SoundElem = &t
+ case constant.Video:
+ t := sdk_struct.VideoElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.VideoElem = &t
+ case constant.File:
+ t := sdk_struct.FileElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.FileElem = &t
+ case constant.AdvancedText:
+ t := sdk_struct.AdvancedTextElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.AdvancedTextElem = &t
+ case constant.AtText:
+ t := sdk_struct.AtTextElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.AtTextElem = &t
+ case constant.Location:
+ t := sdk_struct.LocationElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.LocationElem = &t
+ case constant.Custom:
+ fallthrough
+ case constant.CustomMsgNotTriggerConversation:
+ fallthrough
+ case constant.CustomMsgOnlineOnly:
+ t := sdk_struct.CustomElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.CustomElem = &t
+ case constant.Typing:
+ t := sdk_struct.TypingElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.TypingElem = &t
+ case constant.Quote:
+ t := sdk_struct.QuoteElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.QuoteElem = &t
+ case constant.Merger:
+ t := sdk_struct.MergeElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.MergeElem = &t
+ case constant.Face:
+ t := sdk_struct.FaceElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.FaceElem = &t
+ case constant.Card:
+ t := sdk_struct.CardElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.CardElem = &t
+ case pconstant.Stream:
+ t := sdk_struct.StreamElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.StreamElem = &t
+ default:
+ t := sdk_struct.NotificationElem{}
+ err = utils.JsonStringToStruct(msg.Content, &t)
+ msg.NotificationElem = &t
+ }
+ msg.Content = ""
+
+ return errs.Wrap(err)
+}
+
+func MsgStructToLocalChatLog(message *sdk_struct.MsgStruct) *model_struct.LocalChatLog {
+ localMessage := &model_struct.LocalChatLog{
+ ClientMsgID: message.ClientMsgID,
+ ServerMsgID: message.ServerMsgID,
+ SendID: message.SendID,
+ RecvID: message.RecvID,
+ SenderPlatformID: message.SenderPlatformID,
+ SenderNickname: message.SenderNickname,
+ SenderFaceURL: message.SenderFaceURL,
+ SessionType: message.SessionType,
+ MsgFrom: message.MsgFrom,
+ ContentType: message.ContentType,
+ IsRead: message.IsRead,
+ Status: message.Status,
+ Seq: message.Seq,
+ SendTime: message.SendTime,
+ CreateTime: message.CreateTime,
+ AttachedInfo: message.AttachedInfo,
+ Ex: message.Ex,
+ LocalEx: message.LocalEx,
+ }
+ switch message.ContentType {
+ case constant.Text:
+ localMessage.Content = utils.StructToJsonString(message.TextElem)
+ case constant.Picture:
+ localMessage.Content = utils.StructToJsonString(message.PictureElem)
+ case constant.Sound:
+ localMessage.Content = utils.StructToJsonString(message.SoundElem)
+ case constant.Video:
+ localMessage.Content = utils.StructToJsonString(message.VideoElem)
+ case constant.File:
+ localMessage.Content = utils.StructToJsonString(message.FileElem)
+ case constant.AtText:
+ localMessage.Content = utils.StructToJsonString(message.AtTextElem)
+ case constant.Merger:
+ localMessage.Content = utils.StructToJsonString(message.MergeElem)
+ case constant.Card:
+ localMessage.Content = utils.StructToJsonString(message.CardElem)
+ case constant.Location:
+ localMessage.Content = utils.StructToJsonString(message.LocationElem)
+ case constant.Custom:
+ localMessage.Content = utils.StructToJsonString(message.CustomElem)
+ case constant.Quote:
+ localMessage.Content = utils.StructToJsonString(message.QuoteElem)
+ case constant.Face:
+ localMessage.Content = utils.StructToJsonString(message.FaceElem)
+ case constant.AdvancedText:
+ localMessage.Content = utils.StructToJsonString(message.AdvancedTextElem)
+ case pconstant.Stream:
+ localMessage.Content = utils.StructToJsonString(message.StreamElem)
+ default:
+ localMessage.Content = utils.StructToJsonString(message.NotificationElem)
+ }
+ if message.SessionType == constant.WriteGroupChatType || message.SessionType == constant.ReadGroupChatType {
+ localMessage.RecvID = message.GroupID
+ }
+ localMessage.AttachedInfo = utils.StructToJsonString(message.AttachedInfoElem)
+ return localMessage
+}
diff --git a/internal/conversation_msg/convert.go b/internal/conversation_msg/convert.go
deleted file mode 100644
index 78180d430..000000000
--- a/internal/conversation_msg/convert.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package conversation_msg
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-
- pbConversation "github.com/openimsdk/protocol/conversation"
-)
-
-func ServerConversationToLocal(conversation *pbConversation.Conversation) *model_struct.LocalConversation {
- return &model_struct.LocalConversation{
- ConversationID: conversation.ConversationID,
- ConversationType: conversation.ConversationType,
- UserID: conversation.UserID,
- GroupID: conversation.GroupID,
- RecvMsgOpt: conversation.RecvMsgOpt,
- GroupAtType: conversation.GroupAtType,
- IsPinned: conversation.IsPinned,
- BurnDuration: conversation.BurnDuration,
- IsPrivateChat: conversation.IsPrivateChat,
- AttachedInfo: conversation.AttachedInfo,
- Ex: conversation.Ex,
- MsgDestructTime: conversation.MsgDestructTime,
- IsMsgDestruct: conversation.IsMsgDestruct,
- }
-}
-
-func LocalConversationToServer(conversation *model_struct.LocalConversation) *pbConversation.Conversation {
- return &pbConversation.Conversation{
- ConversationID: conversation.ConversationID,
- ConversationType: conversation.ConversationType,
- UserID: conversation.UserID,
- GroupID: conversation.GroupID,
- RecvMsgOpt: conversation.RecvMsgOpt,
- GroupAtType: conversation.GroupAtType,
- IsPinned: conversation.IsPinned,
- BurnDuration: conversation.BurnDuration,
- IsPrivateChat: conversation.IsPrivateChat,
- AttachedInfo: conversation.AttachedInfo,
- MsgDestructTime: conversation.MsgDestructTime,
- Ex: conversation.Ex,
- IsMsgDestruct: conversation.IsMsgDestruct,
- }
-}
diff --git a/internal/conversation_msg/create_message.go b/internal/conversation_msg/create_message.go
index ace457bea..2212ee8af 100644
--- a/internal/conversation_msg/create_message.go
+++ b/internal/conversation_msg/create_message.go
@@ -17,15 +17,17 @@ package conversation_msg
import (
"context"
"errors"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/tools/log"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"os"
"path/filepath"
"strings"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
)
func (c *Conversation) CreateTextMessage(ctx context.Context, text string) (*sdk_struct.MsgStruct, error) {
@@ -154,303 +156,212 @@ func (c *Conversation) CreateCardMessage(ctx context.Context, card *sdk_struct.C
return &s, nil
}
-func (c *Conversation) CreateVideoMessageFromFullPath(ctx context.Context, videoFullPath string, videoType string,
- duration int64, snapshotFullPath string) (*sdk_struct.MsgStruct, error) {
- dstFile := utils.FileTmpPath(videoFullPath, c.DataDir) //a->b
- written, err := utils.CopyFile(videoFullPath, dstFile)
- if err != nil {
- //log.Error("internal", "open file failed: ", err, videoFullPath)
- return nil, err
- }
- log.ZDebug(ctx, "videoFullPath dstFile", "videoFullPath", videoFullPath,
- "dstFile", dstFile, "written", written)
-
- dstFile = utils.FileTmpPath(snapshotFullPath, c.DataDir) //a->b
- sWritten, err := utils.CopyFile(snapshotFullPath, dstFile)
- if err != nil {
- //log.Error("internal", "open file failed: ", err, snapshotFullPath)
- return nil, err
- }
- log.ZDebug(ctx, "snapshotFullPath dstFile", "snapshotFullPath", snapshotFullPath,
- "dstFile", dstFile, "sWritten", sWritten)
-
+func (c *Conversation) CreateImageMessage(ctx context.Context, imageSourcePath string, sourcePicture, bigPicture, snapshotPicture *sdk_struct.PictureBaseInfo) (*sdk_struct.MsgStruct, error) {
s := sdk_struct.MsgStruct{}
- err = c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Video)
- if err != nil {
- return nil, err
- }
- s.VideoElem = &sdk_struct.VideoElem{
- VideoPath: videoFullPath,
- VideoType: videoType,
- Duration: duration,
- }
- if snapshotFullPath == "" {
- s.VideoElem.SnapshotPath = ""
- } else {
- s.VideoElem.SnapshotPath = snapshotFullPath
- }
- fi, err := os.Stat(s.VideoElem.VideoPath)
+ err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Picture)
if err != nil {
- //log.Error("internal", "get file Attributes error", err.Error())
return nil, err
}
- s.VideoElem.VideoSize = fi.Size()
- if snapshotFullPath != "" {
- imageInfo, err := getImageInfo(s.VideoElem.SnapshotPath)
+
+ // Create by file path
+ if sourcePicture != nil || bigPicture != nil || snapshotPicture != nil {
+ dstFile := utils.FileTmpPath(imageSourcePath, c.DataDir) //a->b
+ _, err := utils.CopyFile(imageSourcePath, dstFile)
if err != nil {
- log.ZError(ctx, "getImageInfo err:", err, "snapshotFullPath", snapshotFullPath)
+ //log.Error(operationID, "open file failed: ", err, imageFullPath)
return nil, err
}
- s.VideoElem.SnapshotHeight = imageInfo.Height
- s.VideoElem.SnapshotWidth = imageInfo.Width
- s.VideoElem.SnapshotSize = imageInfo.Size
- }
- return &s, nil
-}
-func (c *Conversation) CreateFileMessageFromFullPath(ctx context.Context, fileFullPath string, fileName string) (*sdk_struct.MsgStruct, error) {
- dstFile := utils.FileTmpPath(fileFullPath, c.DataDir)
- _, err := utils.CopyFile(fileFullPath, dstFile)
- if err != nil {
- //log.Error("internal", "open file failed: ", err.Error(), fileFullPath)
- return nil, err
+ imageInfo, err := getImageInfo(imageSourcePath)
+ if err != nil {
+ //log.Error(operationID, "getImageInfo err:", err.Error())
+ return nil, err
+ }
- }
- s := sdk_struct.MsgStruct{}
- err = c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.File)
- if err != nil {
- return nil, err
- }
- fi, err := os.Stat(fileFullPath)
- if err != nil {
- //log.Error("internal", "get file Attributes error", err.Error())
- return nil, err
- }
- s.FileElem = &sdk_struct.FileElem{
- FilePath: fileFullPath,
- FileName: fileName,
- FileSize: fi.Size(),
- }
- return &s, nil
-}
-func (c *Conversation) CreateImageMessageFromFullPath(ctx context.Context, imageFullPath string) (*sdk_struct.MsgStruct, error) {
- dstFile := utils.FileTmpPath(imageFullPath, c.DataDir) //a->b
- _, err := utils.CopyFile(imageFullPath, dstFile)
- if err != nil {
- //log.Error(operationID, "open file failed: ", err, imageFullPath)
- return nil, err
- }
- s := sdk_struct.MsgStruct{}
- err = c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Picture)
- if err != nil {
- return nil, err
- }
- imageInfo, err := getImageInfo(imageFullPath)
- if err != nil {
- //log.Error(operationID, "getImageInfo err:", err.Error())
- return nil, err
- }
- s.PictureElem = &sdk_struct.PictureElem{
- SourcePath: imageFullPath,
- SourcePicture: &sdk_struct.PictureBaseInfo{
- Width: imageInfo.Width,
- Height: imageInfo.Height,
- Type: imageInfo.Type,
- },
- }
- return &s, nil
-}
-func (c *Conversation) CreateSoundMessageFromFullPath(ctx context.Context, soundPath string, duration int64) (*sdk_struct.MsgStruct, error) {
- dstFile := utils.FileTmpPath(soundPath, c.DataDir) //a->b
- _, err := utils.CopyFile(soundPath, dstFile)
- if err != nil {
- //log.Error("internal", "open file failed: ", err, soundPath)
- return nil, err
+ s.PictureElem = &sdk_struct.PictureElem{
+ SourcePath: imageSourcePath,
+ SourcePicture: &sdk_struct.PictureBaseInfo{
+ Width: imageInfo.Width,
+ Height: imageInfo.Height,
+ Type: imageInfo.Type,
+ },
+ }
+ } else { // Create by URL
+ s.PictureElem = &sdk_struct.PictureElem{
+ SourcePath: imageSourcePath,
+ SourcePicture: sourcePicture,
+ BigPicture: bigPicture,
+ SnapshotPicture: snapshotPicture,
+ }
}
- s := sdk_struct.MsgStruct{}
- err = c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Sound)
- if err != nil {
- return nil, err
- }
- fi, err := os.Stat(soundPath)
- if err != nil {
- //log.Error("internal", "getSoundInfo err:", err.Error(), s.SoundElem.SoundPath)
- return nil, err
- }
- s.SoundElem = &sdk_struct.SoundElem{
- SoundPath: soundPath,
- Duration: duration,
- DataSize: fi.Size(),
- SoundType: strings.Replace(filepath.Ext(fi.Name()), ".", "", 1),
- }
return &s, nil
}
-func (c *Conversation) CreateImageMessage(ctx context.Context, imagePath string) (*sdk_struct.MsgStruct, error) {
- s := sdk_struct.MsgStruct{}
- err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Picture)
- if err != nil {
- return nil, err
- }
- path := c.DataDir + imagePath
- //path := imagePath
- imageInfo, err := getImageInfo(path)
- if err != nil {
- //log.Error("internal", "get imageInfo err", err.Error())
- return nil, err
- }
- s.PictureElem = &sdk_struct.PictureElem{
- SourcePath: path,
- SourcePicture: &sdk_struct.PictureBaseInfo{
- Width: imageInfo.Width,
- Height: imageInfo.Height,
- Type: imageInfo.Type,
- },
- }
- return &s, nil
-}
-func (c *Conversation) CreateImageMessageByURL(ctx context.Context, sourcePath string, sourcePicture, bigPicture, snapshotPicture sdk_struct.PictureBaseInfo) (*sdk_struct.MsgStruct, error) {
- s := sdk_struct.MsgStruct{}
- err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Picture)
- if err != nil {
- return nil, err
- }
- s.PictureElem = &sdk_struct.PictureElem{
- SourcePath: sourcePath,
- SourcePicture: &sourcePicture,
- BigPicture: &bigPicture,
- SnapshotPicture: &snapshotPicture,
- }
- return &s, nil
-}
-func (c *Conversation) CreateSoundMessageByURL(ctx context.Context, soundElem *sdk_struct.SoundBaseInfo) (*sdk_struct.MsgStruct, error) {
- s := sdk_struct.MsgStruct{}
- err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Sound)
- if err != nil {
- return nil, err
- }
- s.SoundElem = &sdk_struct.SoundElem{
- UUID: soundElem.UUID,
- SoundPath: soundElem.SoundPath,
- SourceURL: soundElem.SourceURL,
- DataSize: soundElem.DataSize,
- Duration: soundElem.Duration,
- SoundType: soundElem.SoundType,
- }
- return &s, nil
-}
-func (c *Conversation) CreateSoundMessage(ctx context.Context, soundPath string, duration int64) (*sdk_struct.MsgStruct, error) {
+func (c *Conversation) CreateSoundMessage(ctx context.Context, soundPath string, duration int64, soundElem *sdk_struct.SoundBaseInfo) (*sdk_struct.MsgStruct, error) {
s := sdk_struct.MsgStruct{}
+
err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Sound)
if err != nil {
return nil, err
}
- path := c.DataDir + soundPath
- fi, err := os.Stat(path)
- if err != nil {
- //log.Error("internal", "get sound info err", err.Error())
- return nil, err
- }
- s.SoundElem = &sdk_struct.SoundElem{
- SoundPath: path,
- Duration: duration,
- DataSize: fi.Size(),
- }
- if typ := strings.Replace(filepath.Ext(fi.Name()), ".", "", 1); typ != "" {
- s.SoundElem.SoundType = "audio/" + strings.ToLower(typ)
- }
- return &s, nil
-}
-func (c *Conversation) CreateVideoMessageByURL(ctx context.Context, videoElem sdk_struct.VideoBaseInfo) (*sdk_struct.MsgStruct, error) {
- s := sdk_struct.MsgStruct{}
- err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Video)
- if err != nil {
- return nil, err
- }
- s.VideoElem = &sdk_struct.VideoElem{
- VideoPath: videoElem.VideoPath,
- VideoUUID: videoElem.VideoUUID,
- VideoURL: videoElem.VideoURL,
- VideoType: videoElem.VideoType,
- VideoSize: videoElem.VideoSize,
- Duration: videoElem.Duration,
- SnapshotPath: videoElem.SnapshotPath,
- SnapshotUUID: videoElem.SnapshotUUID,
- SnapshotSize: videoElem.SnapshotSize,
- SnapshotURL: videoElem.SnapshotURL,
- SnapshotWidth: videoElem.SnapshotWidth,
- SnapshotHeight: videoElem.SnapshotHeight,
- SnapshotType: videoElem.SnapshotType,
+
+ // Create by file path
+ if soundElem == nil {
+ dstFile := utils.FileTmpPath(soundPath, c.DataDir) //a->b
+ _, err := utils.CopyFile(soundPath, dstFile)
+ if err != nil {
+ //log.Error("internal", "open file failed: ", err, soundPath)
+ return nil, err
+ }
+
+ fi, err := os.Stat(soundPath)
+ if err != nil {
+ //log.Error("internal", "getSoundInfo err:", err.Error(), s.SoundElem.SoundPath)
+ return nil, err
+ }
+
+ s.SoundElem = &sdk_struct.SoundElem{
+ SoundPath: soundPath,
+ Duration: duration,
+ DataSize: fi.Size(),
+ SoundType: strings.Replace(filepath.Ext(fi.Name()), ".", "", 1),
+ }
+ } else { // Create by URL
+ s.SoundElem = &sdk_struct.SoundElem{
+ UUID: soundElem.UUID,
+ SoundPath: soundElem.SoundPath,
+ SourceURL: soundElem.SourceURL,
+ DataSize: soundElem.DataSize,
+ Duration: soundElem.Duration,
+ SoundType: soundElem.SoundType,
+ }
}
return &s, nil
}
-func (c *Conversation) CreateVideoMessage(ctx context.Context, videoPath string, videoType string, duration int64, snapshotPath string) (*sdk_struct.MsgStruct, error) {
+
+func (c *Conversation) CreateVideoMessage(ctx context.Context, videoSourcePath string, videoType string, duration int64, snapshotSourcePath string, videoElem *sdk_struct.VideoBaseInfo) (*sdk_struct.MsgStruct, error) {
s := sdk_struct.MsgStruct{}
err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Video)
if err != nil {
return nil, err
}
- s.VideoElem = &sdk_struct.VideoElem{}
- s.VideoElem.VideoPath = c.DataDir + videoPath
- s.VideoElem.VideoType = videoType
- s.VideoElem.Duration = duration
- if snapshotPath == "" {
- s.VideoElem.SnapshotPath = ""
- } else {
- s.VideoElem.SnapshotPath = c.DataDir + snapshotPath
- }
- fi, err := os.Stat(s.VideoElem.VideoPath)
- if err != nil {
- log.ZDebug(ctx, "get video file error", "videoPath", videoPath, "snapshotPath", snapshotPath)
- return nil, err
- }
- s.VideoElem.VideoSize = fi.Size()
- if snapshotPath != "" {
- imageInfo, err := getImageInfo(s.VideoElem.SnapshotPath)
+
+ // Create by file path
+ if videoElem == nil {
+ dstFile := utils.FileTmpPath(videoSourcePath, c.DataDir) //a->b
+ written, err := utils.CopyFile(videoSourcePath, dstFile)
if err != nil {
- //log.Error("internal", "get snapshot info ", err.Error())
+ //log.Error("internal", "open file failed: ", err, videoFullPath)
return nil, err
}
- s.VideoElem.SnapshotHeight = imageInfo.Height
- s.VideoElem.SnapshotWidth = imageInfo.Width
- s.VideoElem.SnapshotSize = imageInfo.Size
+
+ log.ZDebug(ctx, "videoFullPath dstFile", "videoFullPath", videoSourcePath,
+ "dstFile", dstFile, "written", written)
+
+ dstFile = utils.FileTmpPath(snapshotSourcePath, c.DataDir) //a->b
+ sWritten, err := utils.CopyFile(snapshotSourcePath, dstFile)
+ if err != nil {
+ //log.Error("internal", "open file failed: ", err, snapshotFullPath)
+ return nil, err
+ }
+
+ log.ZDebug(ctx, "snapshotFullPath dstFile", "snapshotFullPath", snapshotSourcePath,
+ "dstFile", dstFile, "sWritten", sWritten)
+
+ s.VideoElem = &sdk_struct.VideoElem{
+ VideoPath: videoSourcePath,
+ VideoType: videoType,
+ Duration: duration,
+ }
+
+ if snapshotSourcePath == "" {
+ s.VideoElem.SnapshotPath = ""
+ } else {
+ s.VideoElem.SnapshotPath = snapshotSourcePath
+ }
+
+ fi, err := os.Stat(s.VideoElem.VideoPath)
+ if err != nil {
+ //log.Error("internal", "get file Attributes error", err.Error())
+ return nil, err
+ }
+
+ s.VideoElem.VideoSize = fi.Size()
+ if snapshotSourcePath != "" {
+ imageInfo, err := getImageInfo(s.VideoElem.SnapshotPath)
+ if err != nil {
+ log.ZError(ctx, "getImageInfo err:", err, "snapshotFullPath", snapshotSourcePath)
+ return nil, err
+ }
+
+ s.VideoElem.SnapshotHeight = imageInfo.Height
+ s.VideoElem.SnapshotWidth = imageInfo.Width
+ s.VideoElem.SnapshotSize = imageInfo.Size
+ }
+ } else { // Create by URL
+ s.VideoElem = &sdk_struct.VideoElem{
+ VideoPath: videoElem.VideoPath,
+ VideoUUID: videoElem.VideoUUID,
+ VideoURL: videoElem.VideoURL,
+ VideoType: videoElem.VideoType,
+ VideoSize: videoElem.VideoSize,
+ Duration: videoElem.Duration,
+ SnapshotPath: videoElem.SnapshotPath,
+ SnapshotUUID: videoElem.SnapshotUUID,
+ SnapshotSize: videoElem.SnapshotSize,
+ SnapshotURL: videoElem.SnapshotURL,
+ SnapshotWidth: videoElem.SnapshotWidth,
+ SnapshotHeight: videoElem.SnapshotHeight,
+ SnapshotType: videoElem.SnapshotType,
+ }
}
+
return &s, nil
}
-func (c *Conversation) CreateFileMessageByURL(ctx context.Context, fileElem sdk_struct.FileBaseInfo) (*sdk_struct.MsgStruct, error) {
+
+func (c *Conversation) CreateFileMessage(ctx context.Context, fileSourcePath string, fileName string, fileElem *sdk_struct.FileBaseInfo) (*sdk_struct.MsgStruct, error) {
s := sdk_struct.MsgStruct{}
err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.File)
if err != nil {
return nil, err
}
- s.FileElem = &sdk_struct.FileElem{
- FilePath: fileElem.FilePath,
- UUID: fileElem.UUID,
- SourceURL: fileElem.SourceURL,
- FileName: fileElem.FileName,
- FileSize: fileElem.FileSize,
- FileType: fileElem.FileType,
- }
- return &s, nil
-}
-func (c *Conversation) CreateFileMessage(ctx context.Context, filePath string, fileName string) (*sdk_struct.MsgStruct, error) {
- s := sdk_struct.MsgStruct{FileElem: &sdk_struct.FileElem{}}
- err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.File)
- if err != nil {
- return nil, err
- }
- s.FileElem.FilePath = c.DataDir + filePath
- s.FileElem.FileName = fileName
- fi, err := os.Stat(s.FileElem.FilePath)
- if err != nil {
- //log.Error("internal", "get file message err", err.Error())
- return nil, err
+
+ // Create by file path
+ if fileElem == nil {
+ dstFile := utils.FileTmpPath(fileSourcePath, c.DataDir)
+ _, err := utils.CopyFile(fileSourcePath, dstFile)
+ if err != nil {
+ //log.Error("internal", "open file failed: ", err.Error(), fileFullPath)
+ return nil, err
+
+ }
+
+ fi, err := os.Stat(fileSourcePath)
+ if err != nil {
+ //log.Error("internal", "get file Attributes error", err.Error())
+ return nil, err
+ }
+
+ s.FileElem = &sdk_struct.FileElem{
+ FilePath: fileSourcePath,
+ FileName: fileName,
+ FileSize: fi.Size(),
+ }
+ } else { // Create by URL
+ s.FileElem = &sdk_struct.FileElem{
+ FilePath: fileElem.FilePath,
+ UUID: fileElem.UUID,
+ SourceURL: fileElem.SourceURL,
+ FileName: fileElem.FileName,
+ FileSize: fileElem.FileSize,
+ FileType: fileElem.FileType,
+ }
}
- s.FileElem.FileSize = fi.Size()
- s.Content = utils.StructToJsonString(s.FileElem)
+
return &s, nil
}
+
func (c *Conversation) CreateMergerMessage(ctx context.Context, messages []*sdk_struct.MsgStruct, title string, summaries []string) (*sdk_struct.MsgStruct, error) {
s := sdk_struct.MsgStruct{MergeElem: &sdk_struct.MergeElem{}}
err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Merger)
diff --git a/internal/conversation_msg/delete.go b/internal/conversation_msg/delete.go
index 8cfa3e22c..87d4f119d 100644
--- a/internal/conversation_msg/delete.go
+++ b/internal/conversation_msg/delete.go
@@ -16,7 +16,8 @@ package conversation_msg
import (
"context"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
+ "errors"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
@@ -24,30 +25,35 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/jinzhu/copier"
- pbMsg "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
)
// Delete the local and server
-// Delete the local, do not change the server data
-// To delete the server, you need to change the local message status to delete
-func (c *Conversation) clearConversationFromLocalAndSvr(ctx context.Context, conversationID string, f func(ctx context.Context, conversationID string) error) error {
+func (c *Conversation) clearConversationFromLocalAndServer(ctx context.Context, conversationID string, f func(ctx context.Context, conversationID string) error) error {
_, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
+
// Use conversationID to remove conversations and messages from the server first
- err = c.clearConversationMsgFromSvr(ctx, conversationID)
+ err = c.clearConversationMsgFromServer(ctx, conversationID)
if err != nil {
- return err
+ if errors.Is(errs.Unwrap(err), errs.ErrRecordNotFound) {
+ log.ZWarn(ctx, "clearConversationMsgFromServer err", err, "conversationID", conversationID)
+ } else {
+ return err
+ }
}
+
if err := c.clearConversationAndDeleteAllMsg(ctx, conversationID, false, f); err != nil {
return err
}
+
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{conversationID}}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
+
return nil
}
@@ -72,19 +78,10 @@ func (c *Conversation) clearConversationAndDeleteAllMsg(ctx context.Context, con
return nil
}
-// To delete session information, delete the server first, and then invoke the interface.
-// The client receives a callback to delete all local information.
-func (c *Conversation) clearConversationMsgFromSvr(ctx context.Context, conversationID string) error {
- var apiReq pbMsg.ClearConversationsMsgReq
- apiReq.UserID = c.loginUserID
- apiReq.ConversationIDs = []string{conversationID}
- return util.ApiPost(ctx, constant.ClearConversationMsgRouter, &apiReq, nil)
-}
-
// Delete all messages
-func (c *Conversation) deleteAllMsgFromLocalAndSvr(ctx context.Context) error {
+func (c *Conversation) deleteAllMsgFromLocalAndServer(ctx context.Context) error {
// Delete the server first (high error rate), then delete it.
- err := c.deleteAllMessageFromSvr(ctx)
+ err := c.deleteAllMessageFromServer(ctx)
if err != nil {
return err
}
@@ -96,17 +93,6 @@ func (c *Conversation) deleteAllMsgFromLocalAndSvr(ctx context.Context) error {
return nil
}
-// Delete all server messages
-func (c *Conversation) deleteAllMessageFromSvr(ctx context.Context) error {
- var apiReq pbMsg.UserClearAllMsgReq
- apiReq.UserID = c.loginUserID
- err := util.ApiPost(ctx, constant.ClearAllMsgRouter, &apiReq, nil)
- if err != nil {
- return err
- }
- return nil
-}
-
// Delete all messages from the local
func (c *Conversation) deleteAllMsgFromLocal(ctx context.Context, markDelete bool) error {
conversations, err := c.db.GetAllConversationListDB(ctx)
@@ -130,14 +116,6 @@ func (c *Conversation) deleteAllMsgFromLocal(ctx context.Context, markDelete boo
// Delete a message from the local
func (c *Conversation) deleteMessage(ctx context.Context, conversationID string, clientMsgID string) error {
- if err := c.deleteMessageFromSvr(ctx, conversationID, clientMsgID); err != nil {
- return err
- }
- return c.deleteMessageFromLocal(ctx, conversationID, clientMsgID)
-}
-
-// The user deletes part of the message from the server
-func (c *Conversation) deleteMessageFromSvr(ctx context.Context, conversationID string, clientMsgID string) error {
_, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
if err != nil {
return err
@@ -154,11 +132,12 @@ func (c *Conversation) deleteMessageFromSvr(ctx context.Context, conversationID
log.ZInfo(ctx, "delete msg seq is 0, try again", "msg", localMessage)
return sdkerrs.ErrMsgHasNoSeq
}
- var apiReq pbMsg.DeleteMsgsReq
- apiReq.UserID = c.loginUserID
- apiReq.Seqs = []int64{localMessage.Seq}
- apiReq.ConversationID = conversationID
- return util.ApiPost(ctx, constant.DeleteMsgsRouter, &apiReq, nil)
+ err = c.deleteMessagesFromServer(ctx, conversationID, []int64{localMessage.Seq})
+ if err != nil {
+ return err
+ }
+
+ return c.deleteMessageFromLocal(ctx, conversationID, clientMsgID)
}
// Delete messages from local
@@ -167,9 +146,11 @@ func (c *Conversation) deleteMessageFromLocal(ctx context.Context, conversationI
if err != nil {
return err
}
- if err := c.db.DeleteConversationMsgs(ctx, conversationID, []string{clientMsgID}); err != nil {
+
+ if err := c.db.UpdateColumnsMessage(ctx, conversationID, clientMsgID, map[string]interface{}{"status": constant.MsgStatusHasDeleted}); err != nil {
return err
}
+
if !s.IsRead && s.SendID != c.loginUserID {
if err := c.db.DecrConversationUnreadCount(ctx, conversationID, 1); err != nil {
return err
@@ -177,26 +158,28 @@ func (c *Conversation) deleteMessageFromLocal(ctx context.Context, conversationI
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
}
+
conversation, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
+
var latestMsg sdk_struct.MsgStruct
+ // Convert the latest message in the conversation table.
utils.JsonStringToStruct(conversation.LatestMsg, &latestMsg)
+
if latestMsg.ClientMsgID == clientMsgID {
- log.ZDebug(ctx, "latesetMsg deleted", "seq", latestMsg.Seq, "clientMsgID", latestMsg.ClientMsgID)
- msgs, err := c.db.GetMessageListNoTime(ctx, conversationID, 1, false)
+ log.ZDebug(ctx, "latestMsg deleted", "seq", latestMsg.Seq, "clientMsgID", latestMsg.ClientMsgID)
+ msg, err := c.db.GetLatestActiveMessage(ctx, conversationID, false)
if err != nil {
return err
}
+
latestMsgSendTime := latestMsg.SendTime
latestMsgStr := ""
- if len(msgs) > 0 {
- copier.Copy(&latestMsg, msgs[0])
- err := c.msgConvert(&latestMsg)
- if err != nil {
- log.ZError(ctx, "parsing data error", err, latestMsg)
- }
+ if len(msg) > 0 {
+ latestMsg = *LocalChatLogToMsgStruct(msg[0])
+
latestMsgStr = utils.StructToJsonString(latestMsg)
latestMsgSendTime = latestMsg.SendTime
}
@@ -209,37 +192,39 @@ func (c *Conversation) deleteMessageFromLocal(ctx context.Context, conversationI
return nil
}
-func (c *Conversation) doDeleteMsgs(ctx context.Context, msg *sdkws.MsgData) {
+func (c *Conversation) doDeleteMsgs(ctx context.Context, msg *sdkws.MsgData) error {
tips := sdkws.DeleteMsgsTips{}
utils.UnmarshalNotificationElem(msg.Content, &tips)
log.ZDebug(ctx, "doDeleteMsgs", "seqs", tips.Seqs)
for _, v := range tips.Seqs {
msg, err := c.db.GetMessageBySeq(ctx, tips.ConversationID, v)
if err != nil {
- log.ZError(ctx, "GetMessageBySeq err", err, "conversationID", tips.ConversationID, "seq", v)
+ log.ZWarn(ctx, "GetMessageBySeq err", err, "conversationID", tips.ConversationID, "seq", v)
continue
}
- var s sdk_struct.MsgStruct
- copier.Copy(&s, msg)
- err = c.msgConvert(&s)
- if err != nil {
- log.ZError(ctx, "parsing data error", err, "msg", msg)
- }
if err := c.deleteMessageFromLocal(ctx, tips.ConversationID, msg.ClientMsgID); err != nil {
- log.ZError(ctx, "deleteMessageFromLocal err", err, "conversationID", tips.ConversationID, "seq", v)
+ log.ZWarn(ctx, "deleteMessageFromLocal err", err, "conversationID", tips.ConversationID, "seq", v)
+ return err
}
}
+ return nil
}
-func (c *Conversation) doClearConversations(ctx context.Context, msg *sdkws.MsgData) {
- tips := sdkws.ClearConversationTips{}
- utils.UnmarshalNotificationElem(msg.Content, &tips)
+func (c *Conversation) doClearConversations(ctx context.Context, msg *sdkws.MsgData) error {
+ tips := &sdkws.ClearConversationTips{}
+ err := utils.UnmarshalNotificationElem(msg.Content, tips)
+ if err != nil {
+ return err
+ }
+
log.ZDebug(ctx, "doClearConversations", "tips", tips)
for _, v := range tips.ConversationIDs {
if err := c.clearConversationAndDeleteAllMsg(ctx, v, false, c.db.ClearConversation); err != nil {
- log.ZError(ctx, "clearConversation err", err, "conversationID", v)
+ log.ZWarn(ctx, "clearConversation err", err, "conversationID", v)
+ return err
}
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: tips.ConversationIDs}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
+ return nil
}
diff --git a/internal/conversation_msg/entering.go b/internal/conversation_msg/entering.go
index 5db1055e4..4ea296207 100644
--- a/internal/conversation_msg/entering.go
+++ b/internal/conversation_msg/entering.go
@@ -8,6 +8,7 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ pconstant "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
@@ -34,9 +35,9 @@ func newTyping(c *Conversation) *typing {
send: cache.New(inputStatesSendTime, inputStatesTimeout),
state: cache.New(inputStatesTimeout, inputStatesTimeout),
}
- e.platformIDs = make([]int32, 0, len(constant.PlatformID2Name))
+ e.platformIDs = make([]int32, 0, len(pconstant.PlatformID2Name))
e.platformIDSet = make(map[int32]struct{})
- for id := range constant.PlatformID2Name {
+ for id := range pconstant.PlatformID2Name {
e.platformIDSet[int32(id)] = struct{}{}
e.platformIDs = append(e.platformIDs, int32(id))
}
@@ -164,10 +165,7 @@ func (e *typing) onNewMsg(ctx context.Context, msg *sdkws.MsgData) {
return
}
now := time.Now().UnixMilli()
- expirationTimestamp := msg.SendTime + int64(inputStatesSendTime/time.Millisecond)
- if msg.SendTime > now || expirationTimestamp <= now {
- return
- }
+ expirationTimestamp := now + int64(inputStatesSendTime/time.Millisecond)
var sourceID string
if msg.GroupID == "" {
sourceID = msg.SendID
diff --git a/internal/conversation_msg/image.go b/internal/conversation_msg/image.go
index 309776486..8ae0b6a1d 100644
--- a/internal/conversation_msg/image.go
+++ b/internal/conversation_msg/image.go
@@ -1,16 +1,18 @@
package conversation_msg
import (
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/errs"
- _ "golang.org/x/image/bmp"
- _ "golang.org/x/image/tiff"
- _ "golang.org/x/image/webp"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"os"
+
+ _ "golang.org/x/image/bmp"
+ _ "golang.org/x/image/tiff"
+ _ "golang.org/x/image/webp"
+
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/tools/errs"
)
func getImageInfo(filePath string) (*sdk_struct.ImageInfo, error) {
diff --git a/internal/conversation_msg/sync2.go b/internal/conversation_msg/incremental_sync.go
similarity index 72%
rename from internal/conversation_msg/sync2.go
rename to internal/conversation_msg/incremental_sync.go
index 72205e522..89edfba95 100644
--- a/internal/conversation_msg/sync2.go
+++ b/internal/conversation_msg/incremental_sync.go
@@ -17,16 +17,14 @@ package conversation_msg
import (
"context"
- "github.com/openimsdk/openim-sdk-core/v3/internal/incrversion"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
pbConversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/tools/utils/datautil"
)
func (c *Conversation) IncrSyncConversations(ctx context.Context) error {
- conversationSyncer := incrversion.VersionSynchronizer[*model_struct.LocalConversation, *pbConversation.GetIncrementalConversationResp]{
+ conversationSyncer := syncer.VersionSynchronizer[*model_struct.LocalConversation, *pbConversation.GetIncrementalConversationResp]{
Ctx: ctx,
DB: c.db,
TableName: c.conversationTableName(),
@@ -38,11 +36,7 @@ func (c *Conversation) IncrSyncConversations(ctx context.Context) error {
return c.db.GetAllConversations(ctx)
},
Server: func(version *model_struct.LocalVersionSync) (*pbConversation.GetIncrementalConversationResp, error) {
- return util.CallApi[pbConversation.GetIncrementalConversationResp](ctx, constant.GetIncrementalConversation, &pbConversation.GetIncrementalConversationReq{
- UserID: c.loginUserID,
- Version: version.Version,
- VersionID: version.VersionID,
- })
+ return c.getIncrementalConversationFromServer(ctx, version.Version, version.VersionID)
},
Full: func(resp *pbConversation.GetIncrementalConversationResp) bool {
return resp.Full
@@ -63,12 +57,27 @@ func (c *Conversation) IncrSyncConversations(ctx context.Context) error {
return c.conversationSyncer.Sync(ctx, server, local, nil, true)
},
FullSyncer: func(ctx context.Context) error {
- return c.conversationSyncer.FullSync(ctx, c.loginUserID)
+ conversationIDList, err := c.db.GetAllConversationIDList(ctx)
+ if err != nil {
+ return err
+ }
+ if len(conversationIDList) == 0 {
+ return c.conversationSyncer.FullSync(ctx, c.loginUserID)
+ } else {
+ local, err := c.db.GetAllConversations(ctx)
+ if err != nil {
+ return err
+ }
+ resp, err := c.getAllConversationListFromServer(ctx)
+ if err != nil {
+ return err
+ }
+ server := datautil.Batch(ServerConversationToLocal, resp.Conversations)
+ return c.conversationSyncer.Sync(ctx, server, local, nil, true)
+ }
},
FullID: func(ctx context.Context) ([]string, error) {
- resp, err := util.CallApi[pbConversation.GetFullOwnerConversationIDsResp](ctx, constant.GetFullConversationIDs, &pbConversation.GetFullOwnerConversationIDsReq{
- UserID: c.loginUserID,
- })
+ resp, err := c.getAllConversationIDsFromServer(ctx)
if err != nil {
return nil, err
}
@@ -76,7 +85,7 @@ func (c *Conversation) IncrSyncConversations(ctx context.Context) error {
},
}
- return conversationSyncer.Sync()
+ return conversationSyncer.IncrementalSync()
}
func (c *Conversation) conversationTableName() string {
diff --git a/internal/conversation_msg/message_check.go b/internal/conversation_msg/message_check.go
index fae918405..f4c3206c9 100644
--- a/internal/conversation_msg/message_check.go
+++ b/internal/conversation_msg/message_check.go
@@ -1,27 +1,16 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
package conversation_msg
import (
"context"
"errors"
+ "math/rand"
+ "time"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/log"
@@ -29,100 +18,139 @@ import (
"github.com/openimsdk/protocol/sdkws"
)
-// 检测其内部连续性,如果不连续,则向前补齐,获取这一组消息的最大最小seq,以及需要补齐的seq列表长度
-func (c *Conversation) messageBlocksInternalContinuityCheck(ctx context.Context, conversationID string, notStartTime, isReverse bool, count int,
- startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) (max, min int64, length int) {
+// validateAndFillInternalGaps checks for continuity within a block of messages. If gaps are detected, it initiates a fill operation
+// to retrieve and merge missing messages. It returns the maximum `seq` of this batch, which helps in determining continuity with subsequent batches.
+func (c *Conversation) validateAndFillInternalGaps(ctx context.Context, conversationID string, isReverse bool, count int,
+ startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) int64 {
var lostSeqListLength int
maxSeq, minSeq, haveSeqList := c.getMaxAndMinHaveSeqList(*list)
log.ZDebug(ctx, "getMaxAndMinHaveSeqList is:", "maxSeq", maxSeq, "minSeq", minSeq, "haveSeqList", haveSeqList)
if maxSeq != 0 && minSeq != 0 {
- successiveSeqList := func(max, min int64) (seqList []int64) {
- for i := min; i <= max; i++ {
- seqList = append(seqList, i)
- }
- return seqList
- }(maxSeq, minSeq)
- lostSeqList := utils.DifferenceSubset(successiveSeqList, haveSeqList)
+ lostSeqList := getLostSeqListWithLimitLength(minSeq, maxSeq, haveSeqList, isReverse)
lostSeqListLength = len(lostSeqList)
log.ZDebug(ctx, "get lost seqList is :", "maxSeq", maxSeq, "minSeq", minSeq, "lostSeqList", lostSeqList, "length:", lostSeqListLength)
if lostSeqListLength > 0 {
- var pullSeqList []int64
- if lostSeqListLength <= constant.PullMsgNumForReadDiffusion {
- pullSeqList = lostSeqList
- } else {
- pullSeqList = lostSeqList[lostSeqListLength-constant.PullMsgNumForReadDiffusion : lostSeqListLength]
- }
- c.pullMessageAndReGetHistoryMessages(ctx, conversationID, pullSeqList, notStartTime, isReverse, count, startTime, list, messageListCallback)
+ log.ZDebug(ctx, "messageBlocksInternalContinuityCheck", "lostSeqList", lostSeqList)
+ c.fetchAndMergeMissingMessages(ctx, conversationID, lostSeqList, isReverse, count, startTime, list, messageListCallback)
}
}
- return maxSeq, minSeq, lostSeqListLength
+ if isReverse {
+ return minSeq
+ }
+ return maxSeq
}
-// 检测消息块之间的连续性,如果不连续,则向前补齐,返回块之间是否连续,bool
-func (c *Conversation) messageBlocksBetweenContinuityCheck(ctx context.Context, lastMinSeq, maxSeq int64, conversationID string,
- notStartTime, isReverse bool, count int, startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) bool {
- if lastMinSeq != 0 {
- log.ZDebug(ctx, "get lost LastMinSeq is :", "lastMinSeq", lastMinSeq, "thisMaxSeq", maxSeq)
- if maxSeq != 0 {
- if maxSeq+1 != lastMinSeq {
- startSeq := int64(lastMinSeq) - constant.PullMsgNumForReadDiffusion
- if startSeq <= maxSeq {
- startSeq = int64(maxSeq) + 1
- }
- successiveSeqList := func(max, min int64) (seqList []int64) {
- for i := min; i <= max; i++ {
- seqList = append(seqList, i)
- }
- return seqList
- }(lastMinSeq-1, startSeq)
- log.ZDebug(ctx, "get lost successiveSeqList is :", "successiveSeqList", successiveSeqList, "length:", len(successiveSeqList))
- if len(successiveSeqList) > 0 {
- c.pullMessageAndReGetHistoryMessages(ctx, conversationID, successiveSeqList, notStartTime, isReverse, count, startTime, list, messageListCallback)
- }
- } else {
- return true
- }
-
- } else {
- return true
+// validateAndFillInterBlockGaps checks for continuity between blocks of messages. If a gap is identified, it retrieves the missing messages
+// to bridge the gap. The function returns a boolean indicating whether the blocks are continuous.
+func (c *Conversation) validateAndFillInterBlockGaps(ctx context.Context, thisStartSeq int64, conversationID string,
+ isReverse bool, viewType, count int, startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
+
+ var lastEndSeq, startSeq, endSeq int64
+ var isLostSeq bool
+ if isReverse {
+ lastEndSeq, _ = c.messagePullReverseEndSeqMap.Load(conversationID, viewType)
+ isLostSeq = lastEndSeq+1 != thisStartSeq
+ startSeq = lastEndSeq + 1
+ endSeq = thisStartSeq - 1
+ } else {
+ lastEndSeq, _ = c.messagePullForwardEndSeqMap.Load(conversationID, viewType)
+ isLostSeq = thisStartSeq+1 != lastEndSeq
+ startSeq = thisStartSeq + 1
+ endSeq = lastEndSeq - 1
+ }
+ if isLostSeq && lastEndSeq != 0 {
+ log.ZDebug(ctx, "get lost LastMinSeq is :", "lastEndSeq", lastEndSeq, "thisStartSeq", thisStartSeq, "startSeq", startSeq, "endSeq", endSeq)
+ lostSeqList := getLostSeqListWithLimitLength(startSeq, endSeq, []int64{}, isReverse)
+ log.ZDebug(ctx, "get lost lostSeqList is :", "lostSeqList", lostSeqList, "length:", len(lostSeqList))
+ if len(lostSeqList) > 0 {
+ log.ZDebug(ctx, "messageBlocksBetweenContinuityCheck", "lostSeqList", lostSeqList)
+ c.fetchAndMergeMissingMessages(ctx, conversationID, lostSeqList, isReverse, count, startTime, list, messageListCallback)
}
+ }
+}
- } else {
- return true
+// validateAndFillEndBlockContinuity performs an end-of-block continuity check. If a batch of messages has passed
+// internal and inter-block continuity checks but contains fewer messages than `count`, this function verifies if the end
+// of the message history has been reached. If not, it attempts to retrieve any missing messages to ensure continuity.
+func (c *Conversation) validateAndFillEndBlockContinuity(ctx context.Context, conversationID string,
+ isReverse bool, viewType, count int, startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
+ isShouldFetchMessage, lostSeqList := c.checkEndBlock(ctx, conversationID, isReverse, viewType, count, list, messageListCallback)
+ if isShouldFetchMessage {
+ c.fetchAndMergeMissingMessages(ctx, conversationID, lostSeqList, isReverse, count, startTime, list, messageListCallback)
+ _, _ = c.checkEndBlock(ctx, conversationID, isReverse, viewType, count, list, messageListCallback)
}
- return false
}
+func (c *Conversation) checkEndBlock(ctx context.Context, conversationID string, isReverse bool, viewType, count int,
+ list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) (isShouldFetchMessage bool, seqList []int64) {
+ // Perform an end-of-block check if the retrieved message count is less than requested
+ if len(*list) < count {
+ if isReverse {
+ currentMaxSeq := c.getConversationMaxSeq(ctx, conversationID)
+ maxSeq, _, _ := c.getMaxAndMinHaveSeqList(*list)
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "maxSeq", maxSeq, "conversationID", conversationID, "currentMaxSeq", currentMaxSeq)
+ // Use >= to prevent the currentMaxSeq from being updated too slowly,
+ // which could lead to misjudgments and cause repeated message fetching."
+ if maxSeq >= currentMaxSeq {
+ messageListCallback.IsEnd = true
+ } else {
+ lastEndSeq, _ := c.messagePullReverseEndSeqMap.Load(conversationID, viewType)
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "lastEndSeq", lastEndSeq, "conversationID", conversationID)
+ // If `maxSeq` is zero and `lastEndSeq` is at the maximum server sequence, this batch is fully local
+ if maxSeq == 0 && lastEndSeq >= currentMaxSeq { // All messages in this batch are local messages,
+ // and the maximum seq of the last batch of valid messages has already reached the maximum pullable seq from the server.
+ messageListCallback.IsEnd = true
+ } else {
+ // The batch includes sequences but has not reached the maximum value,
+ // This condition indicates local-only messages, with `maxSeq < maxSeqRecorderMaxSeq` as the only case,
+ // since `lastEndSeq < maxSeqRecorderMaxSeq` is handled in inter-block continuity.
+ lostSeqList := getLostSeqListWithLimitLength(maxSeq+1, currentMaxSeq, []int64{}, isReverse)
+ if len(lostSeqList) > 0 {
+ isShouldFetchMessage = true
+ seqList = lostSeqList
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "lostSeqList", lostSeqList)
+ }
-// 根据最小seq向前补齐消息,由服务器告诉拉取消息结果是否到底,如果网络,则向前补齐,获取这一组消息的最大最小seq,以及需要补齐的seq列表长度
-func (c *Conversation) messageBlocksEndContinuityCheck(ctx context.Context, minSeq int64, conversationID string, notStartTime,
- isReverse bool, count int, startTime int64, list *[]*model_struct.LocalChatLog, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
- if minSeq != 0 {
- seqList := func(seq int64) (seqList []int64) {
- startSeq := seq - constant.PullMsgNumForReadDiffusion
- if startSeq <= 0 {
- startSeq = 1
- }
- log.ZDebug(ctx, "pull start is ", "start seq", startSeq)
- for i := startSeq; i < seq; i++ {
- seqList = append(seqList, i)
+ }
}
- return seqList
- }(minSeq)
- log.ZDebug(ctx, "pull seqList is ", "seqList", seqList, "len", len(seqList))
+ return isShouldFetchMessage, seqList
+ } else {
+ userCanPullMinSeq := c.getConversationMinSeq(ctx, conversationID)
+ _, minSeq, _ := c.getMaxAndMinHaveSeqList(*list)
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "minSeq", minSeq,
+ "conversationID", conversationID, "userCanPullMinSeq", userCanPullMinSeq)
+ // The reason for being less than is that in cases of poor network conditions,
+ // minSeq may be 0, but in fact, the server's sequence has not yet synchronized to the local.
+ if minSeq <= userCanPullMinSeq {
+ messageListCallback.IsEnd = true
+ } else {
+ lastMinSeq, _ := c.messagePullForwardEndSeqMap.Load(conversationID, viewType)
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "lastMinSeq", lastMinSeq, "conversationID", conversationID)
+ // If `minSeq` is zero and `lastMinSeq` is at the minimum server sequence, this batch is fully local
+ if minSeq == 0 && lastMinSeq <= userCanPullMinSeq { // All messages in this batch are local messages,
+ // and the minimum seq of the last batch of valid messages has already reached the minimum pullable seq from the server.
+ messageListCallback.IsEnd = true
+ } else {
+ // The batch includes sequences but has not reached the minimum value,
+ // This condition indicates local-only messages, with `minSeq > userCanPullMinSeq` as the only case,
+ // since `lastMinSeq > userCanPullMinSeq` is handled in inter-block continuity.
+ lostSeqList := getLostSeqListWithLimitLength(userCanPullMinSeq, minSeq-1, []int64{}, isReverse)
+ if len(lostSeqList) > 0 {
+ isShouldFetchMessage = true
+ seqList = lostSeqList
+ log.ZDebug(ctx, "validateAndFillEndBlockContinuity", "lostSeqList", lostSeqList)
+ }
- if len(seqList) > 0 {
- c.pullMessageAndReGetHistoryMessages(ctx, conversationID, seqList, notStartTime, isReverse, count, startTime, list, messageListCallback)
+ }
+ }
+ return isShouldFetchMessage, seqList
}
} else {
- //local don't have messages,本地无消息,但是服务器最大消息不为0
- seqList := []int64{0, 0}
- c.pullMessageAndReGetHistoryMessages(ctx, conversationID, seqList, notStartTime, isReverse, count, startTime, list, messageListCallback)
-
+ messageListCallback.IsEnd = false
+ return isShouldFetchMessage, seqList
}
-
}
func (c *Conversation) getMaxAndMinHaveSeqList(messages []*model_struct.LocalChatLog) (max, min int64, seqList []int64) {
for i := 0; i < len(messages); i++ {
@@ -144,64 +172,134 @@ func (c *Conversation) getMaxAndMinHaveSeqList(messages []*model_struct.LocalCha
return max, min, seqList
}
-// 1、保证单次拉取消息量低于sdk单次从服务器拉取量
-// 2、块中连续性检测
-// 3、块之间连续性检测
-func (c *Conversation) pullMessageAndReGetHistoryMessages(ctx context.Context, conversationID string, seqList []int64,
- notStartTime, isReverse bool, count int, startTime int64, list *[]*model_struct.LocalChatLog,
- messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
- existedSeqList, err := c.db.GetAlreadyExistSeqList(ctx, conversationID, seqList)
- if err != nil {
- log.ZError(ctx, "GetAlreadyExistSeqList err", err, "conversationID", conversationID,
- "seqList", seqList)
- return
+func getLostSeqListWithLimitLength(minSeq, maxSeq int64, haveSeqList []int64, isReverse bool) []int64 {
+ var lostSeqList []int64
+ haveSeqSet := datautil.SliceSetAny(haveSeqList, func(e int64) int64 {
+ return e
+ })
+
+ for i := minSeq; i <= maxSeq; i++ {
+ if _, found := haveSeqSet[i]; !found {
+ lostSeqList = append(lostSeqList, i)
+ }
}
- if len(existedSeqList) == len(seqList) {
- log.ZDebug(ctx, "do not pull message", "seqList", seqList, "existedSeqList", existedSeqList)
- return
+
+ // If the lostSeqList exceeds the max limit, trim the list
+ if len(lostSeqList) > constant.PullMsgNumForReadDiffusion {
+ if isReverse {
+ // If isReverse is true, take the first constant.PullMsgNumForReadDiffusion sequences
+ return lostSeqList[:constant.PullMsgNumForReadDiffusion]
+ } else {
+ // If isReverse is false, take the last constant.PullMsgNumForReadDiffusion sequences
+ return lostSeqList[len(lostSeqList)-constant.PullMsgNumForReadDiffusion:]
+ }
}
- newSeqList := utils.DifferenceSubset(seqList, existedSeqList)
- if len(newSeqList) == 0 {
- log.ZDebug(ctx, "do not pull message", "seqList", seqList, "existedSeqList", existedSeqList,
- "newSeqList", newSeqList)
- return
+
+ // Return the entire lostSeqList if it's within the limit
+ return lostSeqList
+}
+
+// 1. Ensure that the amount of messages pulled at a time is lower than the amount pulled from the server.
+//
+// 2. Check the continuity within a block.
+//
+// 3. Check the continuity between blocks.
+func (c *Conversation) fetchAndMergeMissingMessages(ctx context.Context, conversationID string, seqList []int64,
+ isReverse bool, count int, startTime int64, list *[]*model_struct.LocalChatLog,
+ messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
+
+ var getSeqMessageResp msg.GetSeqMessageResp
+ var getSeqMessageReq msg.GetSeqMessageReq
+ getSeqMessageReq.UserID = c.loginUserID
+ var conversationSeqs msg.ConversationSeqs
+ conversationSeqs.ConversationID = conversationID
+ conversationSeqs.Seqs = seqList
+ getSeqMessageReq.Conversations = append(getSeqMessageReq.Conversations, &conversationSeqs)
+ if isReverse {
+ getSeqMessageReq.Order = sdkws.PullOrder_PullOrderAsc
+ } else {
+ getSeqMessageReq.Order = sdkws.PullOrder_PullOrderDesc
}
- var pullMsgResp sdkws.PullMessageBySeqsResp
- var pullMsgReq sdkws.PullMessageBySeqsReq
- pullMsgReq.UserID = c.loginUserID
- var seqRange sdkws.SeqRange
- seqRange.ConversationID = conversationID
- seqRange.Begin = newSeqList[0]
- seqRange.End = newSeqList[len(newSeqList)-1]
- seqRange.Num = int64(len(newSeqList))
- pullMsgReq.SeqRanges = append(pullMsgReq.SeqRanges, &seqRange)
- log.ZDebug(ctx, "conversation pull message, ", "req", pullMsgReq)
- if notStartTime && !c.LongConnMgr.IsConnected() {
+ log.ZDebug(ctx, "conversation pull message, ", "req", getSeqMessageReq)
+ if startTime == 0 && !c.LongConnMgr.IsConnected() {
return
}
- err = c.SendReqWaitResp(ctx, &pullMsgReq, constant.PullMsgBySeqList, &pullMsgResp)
+ err := c.SendReqWaitResp(ctx, &getSeqMessageReq, constant.PullMsgBySeqList, &getSeqMessageResp)
if err != nil {
- errHandle(newSeqList, list, err, messageListCallback)
- log.ZDebug(ctx, "pullmsg SendReqWaitResp failed", err, "req")
+ errHandle(seqList, list, err, messageListCallback)
+ log.ZWarn(ctx, "pull SendReqWaitResp failed", err, "req")
} else {
- log.ZDebug(ctx, "syncMsgFromServerSplit pull msg", "resp", pullMsgResp)
- if pullMsgResp.Msgs == nil {
+ log.ZDebug(ctx, "syncMsgFromServerSplit pull msg", "resp", getSeqMessageResp)
+ if getSeqMessageResp.Msgs == nil {
log.ZWarn(ctx, "syncMsgFromServerSplit pull msg is null", errors.New("pull message is null"),
- "req", pullMsgReq)
+ "req", getSeqMessageResp.String())
return
}
- if v, ok := pullMsgResp.Msgs[conversationID]; ok {
- c.pullMessageIntoTable(ctx, pullMsgResp.Msgs, conversationID)
- messageListCallback.IsEnd = v.IsEnd
+ if v, ok := getSeqMessageResp.Msgs[conversationID]; ok {
+ c.pullMessageIntoTable(ctx, getSeqMessageResp.Msgs, list)
+ log.ZDebug(ctx, "syncMsgFromServerSplit pull msg success",
+ "conversationID", conversationID, "count", count, "len", len(*list), "msgLen", len(v.Msgs))
+ if v.IsEnd {
+ c.setConversationMinSeq(ctx, isReverse, conversationID, v.EndSeq)
+ }
+ localMessage := datautil.Batch(MsgDataToLocalChatLog, v.Msgs)
+ if !isReverse {
+ reverse(localMessage)
+ }
+ *list = mergeSortedArrays(*list, localMessage, count, !isReverse)
+ }
- if notStartTime {
- *list, err = c.db.GetMessageListNoTime(ctx, conversationID, count, isReverse)
- } else {
- *list, err = c.db.GetMessageList(ctx, conversationID, count, startTime, isReverse)
+ }
+}
+
+func (c *Conversation) getConversationMaxSeq(ctx context.Context, conversationID string) int64 {
+ conversation, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to get conversation", err)
+ return c.maxSeqRecorder.Get(conversationID)
+ }
+ if conversation.MaxSeq == 0 {
+ return c.maxSeqRecorder.Get(conversationID)
+
+ }
+ return conversation.MaxSeq
+}
+func (c *Conversation) getConversationMinSeq(ctx context.Context, conversationID string) int64 {
+ conversation, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to get conversation", err)
+ return 1
+ }
+ if conversation.MinSeq == 0 {
+ return 1
+
+ }
+ return conversation.MinSeq
+}
+func (c *Conversation) setConversationMinSeq(ctx context.Context, isReverse bool, conversationID string, endSeq int64) {
+ conversation, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to get conversation", err)
+ return
+ }
+ if !isReverse {
+ if conversation.MinSeq == 0 || endSeq > conversation.MinSeq {
+ conversation.MinSeq = endSeq
+ }
+ } else {
+ if conversation.MaxSeq == 0 || endSeq < conversation.MaxSeq {
+ conversation.MaxSeq = endSeq
+ err = c.db.UpdateConversation(ctx, conversation)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to update conversation", err)
}
}
}
+ err = c.db.UpdateConversation(ctx, conversation)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to update conversation", err)
+ }
}
func errHandle(seqList []int64, list *[]*model_struct.LocalChatLog, err error, messageListCallback *sdk.GetAdvancedHistoryMessageListCallback) {
messageListCallback.ErrCode = 100
@@ -220,69 +318,207 @@ func errHandle(seqList []int64, list *[]*model_struct.LocalChatLog, err error, m
}
*list = result
}
-func (c *Conversation) pullMessageIntoTable(ctx context.Context, pullMsgData map[string]*sdkws.PullMsgs, conversationID string) {
+
+func mergeSortedArrays(arr1, arr2 []*model_struct.LocalChatLog, n int, isDescending bool) []*model_struct.LocalChatLog {
+ len1 := len(arr1)
+ len2 := len(arr2)
+ result := make([]*model_struct.LocalChatLog, 0, len1+len2)
+
+ i, j := 0, 0
+
+ for i < len1 && j < len2 && len(result) < n {
+ //In descending order, when pulling forward, sort by sendTime. If sendTime is the same, sort by seq.
+ //In ascending order, when pulling backward, sort by sendTime. If sendTime is the same, sort by seq.
+ if (isDescending && (arr1[i].SendTime > arr2[j].SendTime || (arr1[i].SendTime == arr2[j].SendTime && arr1[i].Seq > arr2[j].Seq))) ||
+ (!isDescending && (arr1[i].SendTime < arr2[j].SendTime || (arr1[i].SendTime == arr2[j].SendTime && arr1[i].Seq < arr2[j].Seq))) {
+ result = append(result, arr1[i])
+ i++
+ } else {
+ result = append(result, arr2[j])
+ j++
+ }
+ }
+
+ for i < len1 && len(result) < n {
+ result = append(result, arr1[i])
+ i++
+ }
+
+ for j < len2 && len(result) < n {
+ result = append(result, arr2[j])
+ j++
+ }
+
+ return result
+}
+
+func reverse[T any](arr []T) {
+ for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
+ arr[i], arr[j] = arr[j], arr[i]
+ }
+}
+
+// handleExceptionMessages handles the insertion of exception messages into the local chat log.
+// It identifies and marks messages that fall into the following categories:
+// 1. Messages pulled but marked as deleted, with non-repeating seq, requiring placeholders.
+// 2. Seq jump caused by server downtime, with non-repeating seq, requiring placeholders.
+// 3. Messages sent by the sender with a duplicate ClientMsgID but unique seq.
+// This can occur due to either client-side message duplication or server-side
+// message re-consumption, where the same ClientMsgID is sent again with a different Seq.
+// 4. Concurrent message filling with both duplicate ClientMsgID and seq.
+func (c *Conversation) handleExceptionMessages(ctx context.Context, existingMessage, message *model_struct.LocalChatLog) {
+ var prefix string
+
+ if existingMessage == nil {
+ // Case: The message is marked as deleted
+ if message.Status == constant.MsgStatusHasDeleted {
+ // If ClientMsgID is empty, it's a placeholder for seq gap
+ if message.ClientMsgID == "" {
+ // Gap messages are typically caused by server downtime or prolonged periods of inactivity.
+ // These messages usually lack a message ID, so a message ID needs to be generated to prevent primary key conflicts.
+ message.ClientMsgID = utils.GetMsgID(c.loginUserID)
+ prefix = "[SEQ_GAP_+" + utils.Int64ToString(message.Seq) + "]" // Placeholder for sequence gap
+ } else {
+ prefix = "[DELETED]" // Mark as a deleted message
+ }
+ } else {
+ // For messages that don't fall under known exceptional cases, log as normal
+ prefix = "[UNKNOWN]"
+ log.ZWarn(ctx, "Message is normal, no need to handle", nil, "message", message)
+ }
+ } else {
+ // Case: The message has a duplicate ClientMsgID
+ if existingMessage.Seq == message.Seq {
+ // Case: Both ClientMsgID and Seq are duplicated, it's a concurrent message filling
+ prefix = "[SEQ_DUP]" // Duplicate sequence message, likely caused by concurrent message handling
+ } else {
+ // Case: ClientMsgID is duplicated, but Seq is different, indicating a client-side duplication
+ prefix = "[CLIENT_DUP]" // Client-side resend or server-side consume messages duplication
+ }
+ }
+ getRandomString := func(length int) string {
+ const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+ b := make([]byte, length)
+ for i := range b {
+ b[i] = charset[seededRand.Intn(len(charset))]
+ }
+ return string(b)
+ }
+ // Generate a random suffix to ensure uniqueness
+ randomSuffix := "_" + getRandomString(8)
+
+ // Mark the message as deleted
+ message.Status = constant.MsgStatusHasDeleted
+
+ // Add the exception prefix and random suffix to the ClientMsgID for identification
+ message.ClientMsgID = prefix + message.ClientMsgID + randomSuffix
+}
+
+func (c *Conversation) pullMessageIntoTable(ctx context.Context, pullMsgData map[string]*sdkws.PullMsgs, list *[]*model_struct.LocalChatLog) {
insertMsg := make(map[string][]*model_struct.LocalChatLog, 20)
updateMsg := make(map[string][]*model_struct.LocalChatLog, 30)
var insertMessage, selfInsertMessage, othersInsertMessage []*model_struct.LocalChatLog
var updateMessage []*model_struct.LocalChatLog
- var exceptionMsg []*model_struct.LocalErrChatLog
+ var exceptionMsg []*model_struct.LocalChatLog
log.ZDebug(ctx, "do Msg come here, len: ", "msg length", len(pullMsgData))
for conversationID, msgs := range pullMsgData {
+ msgIDs := datautil.Slice(msgs.Msgs, func(msg *sdkws.MsgData) string {
+ return msg.ClientMsgID
+ })
+ localMessages, err := c.db.GetMessagesByClientMsgIDs(ctx, conversationID, msgIDs)
+ if err != nil {
+ log.ZWarn(ctx, "Failed to get messages by ClientMsgIDs", err)
+ }
+ processedMsgIDs := make(map[string]*model_struct.LocalChatLog, len(msgs.Msgs))
+ localMessagesMap := datautil.SliceToMap(localMessages, func(msg *model_struct.LocalChatLog) string { return msg.ClientMsgID })
for _, v := range msgs.Msgs {
log.ZDebug(ctx, "msg detail", "msg", v, "conversationID", conversationID)
- msg := c.msgDataToLocalChatLog(v)
- //When the message has been marked and deleted by the cloud, it is directly inserted locally without any conversation and message update.
- if msg.Status == constant.MsgStatusHasDeleted {
+ //When the message has been marked and deleted by the cloud, it is directly inserted locally
+ //without any conversation and message update.
+ msg := MsgDataToLocalChatLog(v)
+ if existingMessage, ok := processedMsgIDs[v.ClientMsgID]; ok {
+ c.handleExceptionMessages(ctx, existingMessage, msg)
+ v.Status = msg.Status
+ exceptionMsg = append(exceptionMsg, msg)
insertMessage = append(insertMessage, msg)
continue
}
- msg.Status = constant.MsgStatusSendSuccess
- // log.Info(operationID, "new msg, seq, ServerMsgID, ClientMsgID", msg.Seq, msg.ServerMsgID, msg.ClientMsgID)
- //De-analyze data
- if msg.ClientMsgID == "" {
- exceptionMsg = append(exceptionMsg, c.msgDataToLocalErrChatLog(msg))
+ if v.Status == constant.MsgStatusHasDeleted {
+ c.handleExceptionMessages(ctx, nil, msg)
+ v.ClientMsgID = msg.ClientMsgID
+ exceptionMsg = append(exceptionMsg, msg)
+ insertMessage = append(insertMessage, msg)
continue
}
+ existingMsg, exists := localMessagesMap[msg.ClientMsgID]
if v.SendID == c.loginUserID { //seq
// Messages sent by myself //if sent through this terminal
- m, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID)
- if err == nil {
- log.ZInfo(ctx, "have message", "msg", msg)
- if m.Seq == 0 {
+ if exists {
+ log.ZDebug(ctx, "have message", "msg", msg)
+ if existingMsg.Seq == 0 {
+ //If the message sent by the user hasn't synchronized the seq to the local storage in time,
+ //during the next sync, there will be local messages with seq as 0. These messages need to be updated with the correct seq and deduplicated.
updateMessage = append(updateMessage, msg)
} else {
- exceptionMsg = append(exceptionMsg, c.msgDataToLocalErrChatLog(msg))
+ // The message you sent is duplicated, possibly due to a resend or the server consuming
+ // the message multiple times.
+ c.handleExceptionMessages(ctx, existingMsg, msg)
+ v.Status = msg.Status
+ exceptionMsg = append(exceptionMsg, msg)
+ insertMessage = append(insertMessage, msg)
}
} else { // send through other terminal
- log.ZInfo(ctx, "sync message", "msg", msg)
+ log.ZDebug(ctx, "sync message", "msg", msg)
selfInsertMessage = append(selfInsertMessage, msg)
}
} else { //Sent by others
- if oldMessage, err := c.db.GetMessage(ctx, conversationID, msg.ClientMsgID); err != nil { //Deduplication operation
+ if !exists {
othersInsertMessage = append(othersInsertMessage, msg)
} else {
- if oldMessage.Seq == 0 {
- updateMessage = append(updateMessage, msg)
- }
+ // The message sent by others is duplicated, possibly due to a resend or the server consuming
+ // the message multiple times.
+ c.handleExceptionMessages(ctx, existingMsg, msg)
+ v.Status = msg.Status
+ exceptionMsg = append(exceptionMsg, msg)
+ insertMessage = append(insertMessage, msg)
}
}
-
+ processedMsgIDs[msg.ClientMsgID] = msg
}
+ timeNow := time.Now()
insertMsg[conversationID] = append(insertMessage, c.faceURLAndNicknameHandle(ctx, selfInsertMessage, othersInsertMessage, conversationID)...)
updateMsg[conversationID] = updateMessage
+ log.ZDebug(ctx, "faceURLAndNicknameHandle, ", "cost time", time.Since(timeNow).Milliseconds(),
+ "updateMsg", updateMessage, "insertMsg", insertMessage, "selfInsertMessage", selfInsertMessage, "othersInsertMessage", othersInsertMessage)
//update message
- if err6 := c.messageController.BatchUpdateMessageList(ctx, updateMsg); err6 != nil {
+ if err6 := c.batchUpdateMessageList(ctx, updateMsg); err6 != nil {
log.ZError(ctx, "sync seq normal message err :", err6)
}
- b3 := utils.GetCurrentTimestampByMill()
+ if len(updateMessage) > 0 {
+ updateMessageMap := datautil.SliceToMap(updateMessage, func(message *model_struct.LocalChatLog) string {
+ return message.ClientMsgID
+ })
+
+ filteredList := make([]*model_struct.LocalChatLog, 0, len(*list))
+ for _, v := range *list {
+ if _, ok := updateMessageMap[v.ClientMsgID]; !ok {
+ filteredList = append(filteredList, v)
+ }
+ }
+
+ *list = filteredList
+ }
+
+ timeNow = time.Now()
//Normal message storage
- _ = c.messageController.BatchInsertMessageList(ctx, insertMsg)
- b4 := utils.GetCurrentTimestampByMill()
- log.ZDebug(ctx, "BatchInsertMessageListController, ", "cost time", b4-b3)
+ _ = c.batchInsertMessageList(ctx, insertMsg)
+ log.ZDebug(ctx, "BatchInsertMessageListController, ", "cost time", time.Since(timeNow).Milliseconds())
//Exception message storage
for _, v := range exceptionMsg {
@@ -292,10 +528,21 @@ func (c *Conversation) pullMessageIntoTable(ctx context.Context, pullMsgData map
}
}
-// 拉取的消息都需要经过块内部连续性检测以及块和上一块之间的连续性检测不连续则补,补齐的过程中如果出现任何异常只给seq从大到小到断层
-// 拉取消息不满量,获取服务器中该群最大seq以及用户对于此群最小seq,本地该群的最小seq,如果本地的不为0并且小于等于服务器最小的,说明已经到底部
-// 如果本地的为0,可以理解为初始化的时候,数据还未同步,或者异常情况,如果服务器最大seq-服务器最小seq>=0说明还未到底部,否则到底部
-
+// All pulled messages must undergo continuity checks within the block and between the current block and the previous
+// one. If discontinuity is detected, the gaps should be filled. During the gap-filling process, if any exceptions occur,
+// only provide the sequence numbers in descending order up to the break.
+// When the pulled messages are less than expected, retrieve the server's maximum sequence (seq) for the group, the
+// user's minimum seq for the group, and the local minimum seq for the group. If the local seq is not zero and is less
+// than or equal to the server's minimum seq, it indicates that the bottom has been reached. If the local seq is zero,
+// it can be understood as an initialization where the data hasn't been synchronized yet, or there is an exceptional
+// case. If the difference between the server's maximum seq and minimum seq is greater than or equal to zero, it
+// indicates that the bottom hasn't been reached. Otherwise, the bottom has been reached.
+
+// faceURLAndNicknameHandle handles the assignment of face URLs and nicknames for chat logs
+// based on the conversation type (single chat or group chat).
+// It first retrieves the conversation information using the provided conversationID.
+// Depending on the conversation type, it delegates the handling to either singleHandle (for single chats)
+// or groupHandle (for group chats). If conversation information retrieval fails, it returns the merged chat logs.
func (c *Conversation) faceURLAndNicknameHandle(ctx context.Context, self, others []*model_struct.LocalChatLog, conversationID string) []*model_struct.LocalChatLog {
lc, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
@@ -304,43 +551,53 @@ func (c *Conversation) faceURLAndNicknameHandle(ctx context.Context, self, other
switch lc.ConversationType {
case constant.SingleChatType:
c.singleHandle(ctx, self, others, lc)
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
c.groupHandle(ctx, self, others, lc)
}
return append(self, others...)
}
+
+// singleHandle processes chat logs for single chat conversations.
+// It updates the SenderFaceURL and SenderNickname fields for messages in the `self` list
+// using the logged-in user's information, and for messages in the `others` list
+// using the other party's information if available in the conversation.
func (c *Conversation) singleHandle(ctx context.Context, self, others []*model_struct.LocalChatLog, lc *model_struct.LocalConversation) {
- userInfo, err := c.db.GetLoginUser(ctx, c.loginUserID)
- if err == nil {
- for _, chatLog := range self {
- chatLog.SenderFaceURL = userInfo.FaceURL
- chatLog.SenderNickname = userInfo.Nickname
+ if len(self) > 0 {
+ userInfo, err := c.db.GetLoginUser(ctx, c.loginUserID)
+ if err == nil {
+ for _, chatLog := range self {
+ chatLog.SenderFaceURL = userInfo.FaceURL
+ chatLog.SenderNickname = userInfo.Nickname
+ }
}
}
+
if lc.FaceURL != "" && lc.ShowName != "" {
for _, chatLog := range others {
chatLog.SenderFaceURL = lc.FaceURL
chatLog.SenderNickname = lc.ShowName
}
}
-
}
+
+// groupHandle processes chat logs for group chat conversations.
+// It merges the `self` and `others` chat logs and updates the SenderFaceURL and SenderNickname fields
+// using the group members' information. If group member information is not available,
+// it attempts to retrieve the sender's information from a local cache.
func (c *Conversation) groupHandle(ctx context.Context, self, others []*model_struct.LocalChatLog, lc *model_struct.LocalConversation) {
allMessage := append(self, others...)
allSenders := datautil.Slice(allMessage, func(e *model_struct.LocalChatLog) string {
return e.SendID
})
- localGroupMemberInfo, err := c.group.GetSpecifiedGroupMembersInfo(ctx, lc.GroupID, datautil.Distinct(allSenders))
+ groupMap, err := c.group.GetGroupMemberNameAndFaceURL(ctx, lc.GroupID, datautil.Distinct(allSenders))
if err != nil {
log.ZError(ctx, "get group member info err", err)
return
}
- groupMap := datautil.SliceToMap(localGroupMemberInfo, func(e *model_struct.LocalGroupMember) string {
- return e.UserID
- })
for _, chatLog := range allMessage {
if g, ok := groupMap[chatLog.SendID]; ok { // If group member info is successfully retrieved
+ log.ZDebug(ctx, "find in GetGroupMemberNameAndFaceURL", "sendID", chatLog.SendID, "faceURL", g.FaceURL, "nickName", g.Nickname)
if g.FaceURL != "" && g.Nickname != "" {
chatLog.SenderFaceURL = g.FaceURL
chatLog.SenderNickname = g.Nickname
@@ -350,10 +607,10 @@ func (c *Conversation) groupHandle(ctx context.Context, self, others []*model_st
if err != nil {
log.ZWarn(ctx, "getUserNameAndFaceURL error", err, "senderID", chatLog.SendID)
} else if faceURL != "" && name != "" {
+ log.ZDebug(ctx, "find in getUserNameAndFaceURL", "sendID", chatLog.SendID, "faceURL", faceURL, "nickName", name)
chatLog.SenderFaceURL = faceURL
chatLog.SenderNickname = name
}
}
}
-
}
diff --git a/internal/conversation_msg/message_check_test.go b/internal/conversation_msg/message_check_test.go
new file mode 100644
index 000000000..5dfd1123d
--- /dev/null
+++ b/internal/conversation_msg/message_check_test.go
@@ -0,0 +1,258 @@
+package conversation_msg
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+)
+
+func TestMergeSortedArrays(t *testing.T) {
+ array := []*model_struct.LocalChatLog{
+ {SendTime: 2, Content: "Message 2"},
+ {SendTime: 4, Content: "Message 4"},
+ {SendTime: 6, Content: "Message 6"},
+ }
+ reverse(array)
+
+ tests := []struct {
+ arr1, arr2 []*model_struct.LocalChatLog
+ n int
+ isDescending bool
+ expected []*model_struct.LocalChatLog
+ }{
+ {
+ // Test merging two descending arrays
+ arr1: []*model_struct.LocalChatLog{
+ {SendTime: 9, Content: "Message 9"},
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 3, Content: "Message 3"},
+ },
+ arr2: []*model_struct.LocalChatLog{
+ {SendTime: 8, Content: "Message 8"},
+ {SendTime: 6, Content: "Message 6"},
+ {SendTime: 2, Content: "Message 2"},
+ },
+ n: 4, // Limit result to first 4 elements
+ isDescending: true,
+ expected: []*model_struct.LocalChatLog{
+ {SendTime: 9, Content: "Message 9"},
+ {SendTime: 8, Content: "Message 8"},
+ {SendTime: 6, Content: "Message 6"},
+ {SendTime: 5, Content: "Message 5"},
+ },
+ },
+ {
+ // Test merging an empty array and a descending array
+ arr1: []*model_struct.LocalChatLog{},
+ arr2: []*model_struct.LocalChatLog{
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 3, Content: "Message 3"},
+ {SendTime: 1, Content: "Message 1"},
+ },
+ n: 3,
+ isDescending: true,
+ expected: []*model_struct.LocalChatLog{
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 3, Content: "Message 3"},
+ {SendTime: 1, Content: "Message 1"},
+ },
+ },
+ {
+ // Test merging two empty arrays
+ arr1: []*model_struct.LocalChatLog{},
+ arr2: []*model_struct.LocalChatLog{},
+ n: 0,
+ isDescending: true,
+ expected: []*model_struct.LocalChatLog{},
+ },
+ {
+ // Test merging a descending array and an ascending array
+ arr1: []*model_struct.LocalChatLog{
+ {SendTime: 7, Content: "Message 7"},
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 3, Content: "Message 3"},
+ },
+ arr2: array,
+ n: 5, // Limit result to first 5 elements
+ isDescending: true,
+ // Expected result: merged in descending order
+ expected: []*model_struct.LocalChatLog{
+ {SendTime: 7, Content: "Message 7"},
+ {SendTime: 6, Content: "Message 6"},
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 4, Content: "Message 4"},
+ {SendTime: 3, Content: "Message 3"},
+ },
+ },
+
+ {
+ // Test merging a descending array and an ascending array
+ arr1: []*model_struct.LocalChatLog{
+ {SendTime: 1, Content: "Message 1"},
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 7, Content: "Message 7"},
+ },
+ arr2: []*model_struct.LocalChatLog{
+ {SendTime: 2, Content: "Message 2"},
+ {SendTime: 6, Content: "Message 6"},
+ {SendTime: 9, Content: "Message 9"},
+ },
+ n: 5, // Limit result to first 5 elements
+ isDescending: false,
+ // Expected result: merged in descending order
+ expected: []*model_struct.LocalChatLog{
+ {SendTime: 1, Content: "Message 1"},
+ {SendTime: 2, Content: "Message 2"},
+ {SendTime: 5, Content: "Message 5"},
+ {SendTime: 6, Content: "Message 6"},
+ {SendTime: 7, Content: "Message 7"},
+ },
+ },
+ {
+ // Test merging a descending array and an ascending array
+ arr1: []*model_struct.LocalChatLog{
+ {SendTime: 0, Content: "Message 54", Seq: 54},
+ {SendTime: 0, Content: "Message 53", Seq: 53},
+ {SendTime: 0, Content: "Message 52", Seq: 52},
+ {SendTime: 0, Content: "Message 4", Seq: 4},
+ },
+ arr2: []*model_struct.LocalChatLog{
+ {SendTime: 0, Content: "Message 51", Seq: 51},
+ {SendTime: 0, Content: "Message 50", Seq: 50},
+ {SendTime: 0, Content: "Message 49", Seq: 49},
+ {SendTime: 0, Content: "Message 48", Seq: 48},
+ {SendTime: 0, Content: "Message 47", Seq: 47},
+ {SendTime: 0, Content: "Message 46", Seq: 46},
+ {SendTime: 0, Content: "Message 45", Seq: 45},
+ {SendTime: 0, Content: "Message 44", Seq: 44},
+ {SendTime: 0, Content: "Message 43", Seq: 43},
+ {SendTime: 0, Content: "Message 42", Seq: 42},
+ {SendTime: 0, Content: "Message 41", Seq: 41},
+ {SendTime: 0, Content: "Message 40", Seq: 40},
+ {SendTime: 0, Content: "Message 39", Seq: 39},
+ {SendTime: 0, Content: "Message 38", Seq: 38},
+ {SendTime: 0, Content: "Message 37", Seq: 37},
+ {SendTime: 0, Content: "Message 36", Seq: 36},
+ {SendTime: 0, Content: "Message 35", Seq: 35},
+ {SendTime: 0, Content: "Message 34", Seq: 34},
+ {SendTime: 0, Content: "Message 33", Seq: 33},
+ {SendTime: 0, Content: "Message 32", Seq: 32},
+ {SendTime: 0, Content: "Message 31", Seq: 31},
+ {SendTime: 0, Content: "Message 30", Seq: 30},
+ {SendTime: 0, Content: "Message 29", Seq: 29},
+ {SendTime: 0, Content: "Message 28", Seq: 28},
+ {SendTime: 0, Content: "Message 27", Seq: 27},
+ {SendTime: 0, Content: "Message 26", Seq: 26},
+ {SendTime: 0, Content: "Message 25", Seq: 25},
+ {SendTime: 0, Content: "Message 24", Seq: 24},
+ {SendTime: 0, Content: "Message 23", Seq: 23},
+ {SendTime: 0, Content: "Message 22", Seq: 22},
+ {SendTime: 0, Content: "Message 21", Seq: 21},
+ {SendTime: 0, Content: "Message 20", Seq: 20},
+ {SendTime: 0, Content: "Message 19", Seq: 19},
+ {SendTime: 0, Content: "Message 18", Seq: 18},
+ {SendTime: 0, Content: "Message 17", Seq: 17},
+ {SendTime: 0, Content: "Message 16", Seq: 16},
+ {SendTime: 0, Content: "Message 15", Seq: 15},
+ {SendTime: 0, Content: "Message 14", Seq: 14},
+ {SendTime: 0, Content: "Message 13", Seq: 13},
+ {SendTime: 0, Content: "Message 12", Seq: 12},
+ {SendTime: 0, Content: "Message 11", Seq: 11},
+ {SendTime: 0, Content: "Message 10", Seq: 10},
+ {SendTime: 0, Content: "Message 9", Seq: 9},
+ {SendTime: 0, Content: "Message 8", Seq: 8},
+ {SendTime: 0, Content: "Message 7", Seq: 7},
+ {SendTime: 0, Content: "Message 6", Seq: 6},
+ {SendTime: 0, Content: "Message 5", Seq: 5},
+ },
+ n: 20, // Limit result to first 5 elements
+ isDescending: true,
+ // Expected result: merged in descending order
+ expected: []*model_struct.LocalChatLog{
+ {SendTime: 0, Content: "Message 54", Seq: 54},
+ {SendTime: 0, Content: "Message 53", Seq: 53},
+ {SendTime: 0, Content: "Message 52", Seq: 52},
+ {SendTime: 0, Content: "Message 51", Seq: 51},
+ {SendTime: 0, Content: "Message 50", Seq: 50},
+ {SendTime: 0, Content: "Message 49", Seq: 49},
+ {SendTime: 0, Content: "Message 48", Seq: 48},
+ {SendTime: 0, Content: "Message 47", Seq: 47},
+ {SendTime: 0, Content: "Message 46", Seq: 46},
+ {SendTime: 0, Content: "Message 45", Seq: 45},
+ {SendTime: 0, Content: "Message 44", Seq: 44},
+ {SendTime: 0, Content: "Message 43", Seq: 43},
+ {SendTime: 0, Content: "Message 42", Seq: 42},
+ {SendTime: 0, Content: "Message 41", Seq: 41},
+ {SendTime: 0, Content: "Message 40", Seq: 40},
+ {SendTime: 0, Content: "Message 39", Seq: 39},
+ {SendTime: 0, Content: "Message 38", Seq: 38},
+ {SendTime: 0, Content: "Message 37", Seq: 37},
+ {SendTime: 0, Content: "Message 36", Seq: 36},
+ {SendTime: 0, Content: "Message 35", Seq: 35},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ result := mergeSortedArrays(tt.arr1, tt.arr2, tt.n, tt.isDescending)
+ if !reflect.DeepEqual(result, tt.expected) {
+ t.Errorf(
+ "mergeSortedArrays(%v, %v, %d) = %v; want %v",
+ extractSendSeqs(tt.arr1),
+ extractSendSeqs(tt.arr2),
+ tt.n,
+ extractSendSeqs(result),
+ extractSendSeqs(tt.expected),
+ )
+ } else {
+ fmt.Printf(
+ "PASS: mergeSortedArrays(%v, %v, %d) = %v\n",
+ extractSendSeqs(tt.arr1),
+ extractSendSeqs(tt.arr2),
+ tt.n,
+ extractSendSeqs(result),
+ )
+ }
+ }
+}
+
+func TestReverse(t *testing.T) {
+ tests := []struct {
+ input []int
+ expected []int
+ }{
+ {input: []int{1, 2, 3, 4, 5}, expected: []int{5, 4, 3, 2, 1}},
+ {input: []int{10, 20, 30}, expected: []int{30, 20, 10}},
+ {input: []int{1, 2}, expected: []int{2, 1}},
+ {input: []int{100}, expected: []int{100}},
+ {input: []int{}, expected: []int{}},
+ }
+
+ for _, tt := range tests {
+
+ inputCopy := make([]int, len(tt.input))
+ copy(inputCopy, tt.input)
+
+ reverse(inputCopy)
+ if !reflect.DeepEqual(inputCopy, tt.expected) {
+ t.Errorf("reverse(%v) = %v; want %v", tt.input, inputCopy, tt.expected)
+ }
+ }
+}
+
+func extractSendTimes(arr []*model_struct.LocalChatLog) []int64 {
+ sendTimes := make([]int64, len(arr))
+ for i, log := range arr {
+ sendTimes[i] = log.SendTime
+ }
+ return sendTimes
+}
+func extractSendSeqs(arr []*model_struct.LocalChatLog) []int64 {
+ sendTimes := make([]int64, len(arr))
+ for i, log := range arr {
+ sendTimes[i] = log.Seq
+ }
+ return sendTimes
+}
diff --git a/internal/conversation_msg/message_controller.go b/internal/conversation_msg/message_controller.go
deleted file mode 100644
index bf8f79d16..000000000
--- a/internal/conversation_msg/message_controller.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package conversation_msg
-
-import (
- "context"
- "encoding/json"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
-
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-type MessageController struct {
- db db_interface.DataBase
- ch chan common.Cmd2Value
-}
-
-func NewMessageController(db db_interface.DataBase, ch chan common.Cmd2Value) *MessageController {
- return &MessageController{db: db, ch: ch}
-}
-func (m *MessageController) BatchUpdateMessageList(ctx context.Context, updateMsg map[string][]*model_struct.LocalChatLog) error {
- if updateMsg == nil {
- return nil
- }
- for conversationID, messages := range updateMsg {
- conversation, err := m.db.GetConversation(ctx, conversationID)
- if err != nil {
- log.ZError(ctx, "GetConversation err", err, "conversationID", conversationID)
- continue
- }
- latestMsg := &sdk_struct.MsgStruct{}
- if err := json.Unmarshal([]byte(conversation.LatestMsg), latestMsg); err != nil {
- log.ZError(ctx, "Unmarshal err", err, "conversationID",
- conversationID, "latestMsg", conversation.LatestMsg, "messages", messages)
- continue
- }
- for _, v := range messages {
- v1 := new(model_struct.LocalChatLog)
- v1.ClientMsgID = v.ClientMsgID
- v1.Seq = v.Seq
- v1.Status = v.Status
- v1.RecvID = v.RecvID
- v1.SessionType = v.SessionType
- v1.ServerMsgID = v.ServerMsgID
- v1.SendTime = v.SendTime
- err := m.db.UpdateMessage(ctx, conversationID, v1)
- if err != nil {
- return utils.Wrap(err, "BatchUpdateMessageList failed")
- }
- if latestMsg.ClientMsgID == v.ClientMsgID {
- latestMsg.ServerMsgID = v.ServerMsgID
- latestMsg.Seq = v.Seq
- latestMsg.SendTime = v.SendTime
- latestMsg.Status = v.Status
- conversation.LatestMsg = utils.StructToJsonString(latestMsg)
- _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversation.ConversationID,
- Action: constant.AddConOrUpLatMsg, Args: *conversation}, m.ch)
-
- }
- }
-
- }
- return nil
-}
-
-func (m *MessageController) BatchInsertMessageList(ctx context.Context, insertMsg map[string][]*model_struct.LocalChatLog) error {
- if insertMsg == nil {
- return nil
- }
- for conversationID, messages := range insertMsg {
- if len(messages) == 0 {
- continue
- }
- err := m.db.BatchInsertMessageList(ctx, conversationID, messages)
- if err != nil {
- log.ZError(ctx, "insert GetMessage detail err:", err, "conversationID", conversationID, "messages", messages)
- for _, v := range messages {
- e := m.db.InsertMessage(ctx, conversationID, v)
- if e != nil {
- log.ZError(ctx, "InsertMessage err", err, "conversationID", conversationID, "message", v)
- }
- }
- }
-
- }
- return nil
-}
-
-func (c *Conversation) PullMessageBySeqs(ctx context.Context, seqs []*sdkws.SeqRange) (*sdkws.PullMessageBySeqsResp, error) {
- return util.CallApi[sdkws.PullMessageBySeqsResp](ctx, constant.PullUserMsgBySeqRouter, sdkws.PullMessageBySeqsReq{UserID: c.loginUserID, SeqRanges: seqs})
-}
-func (m *MessageController) SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, keywordList []string,
- keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
- var list []*model_struct.LocalChatLog
- conversationIDList, err := m.db.GetAllConversationIDList(ctx)
- for _, v := range conversationIDList {
- sList, err := m.db.SearchMessageByContentTypeAndKeyword(ctx, contentType, v, keywordList, keywordListMatchType, startTime, endTime)
- if err != nil {
- // TODO: log.Error(operationID, "search message in group err", err.Error(), v)
- continue
- }
- list = append(list, sList...)
- }
-
- return list, nil
-}
diff --git a/internal/conversation_msg/notification.go b/internal/conversation_msg/notification.go
new file mode 100644
index 000000000..8e6153d74
--- /dev/null
+++ b/internal/conversation_msg/notification.go
@@ -0,0 +1,538 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package conversation_msg
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ pconstant "github.com/openimsdk/protocol/constant"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+)
+
+const (
+ syncWait = iota
+ asyncNoWait
+ asyncWait
+)
+
+// InitSyncProgress is initialize Sync when reinstall.
+const InitSyncProgress = 10
+
+func (c *Conversation) Work(c2v common.Cmd2Value) {
+ log.ZDebug(c2v.Ctx, "NotificationCmd start", "caller", c2v.Caller, "cmd", c2v.Cmd, "value", c2v.Value)
+ defer log.ZDebug(c2v.Ctx, "NotificationCmd end", "caller", c2v.Caller, "cmd", c2v.Cmd, "value", c2v.Value)
+ switch c2v.Cmd {
+ case constant.CmdNewMsgCome:
+ c.doMsgNew(c2v)
+ case constant.CmdUpdateConversation:
+ c.doUpdateConversation(c2v)
+ case constant.CmdUpdateMessage:
+ c.doUpdateMessage(c2v)
+ case constant.CmSyncReactionExtensions:
+ case constant.CmdNotification:
+ c.doNotificationManager(c2v)
+ case constant.CmdSyncData:
+ c.syncData(c2v)
+ case constant.CmdSyncFlag:
+ c.syncFlag(c2v)
+ case constant.CmdMsgSyncInReinstall:
+ c.doMsgSyncByReinstalled(c2v)
+ }
+}
+
+func (c *Conversation) syncFlag(c2v common.Cmd2Value) {
+ ctx := c2v.Ctx
+ syncFlag := c2v.Value.(sdk_struct.CmdNewMsgComeToConversation).SyncFlag
+ switch syncFlag {
+ case constant.AppDataSyncStart:
+ log.ZDebug(ctx, "AppDataSyncStart")
+ c.startTime = time.Now()
+ c.ConversationListener().OnSyncServerStart(true)
+ c.ConversationListener().OnSyncServerProgress(1)
+ asyncWaitFunctions := []func(c context.Context) error{
+ c.group.SyncAllJoinedGroupsAndMembers,
+ c.relation.IncrSyncFriends,
+ }
+ runSyncFunctions(ctx, asyncWaitFunctions, asyncWait)
+ c.addInitProgress(InitSyncProgress * 4 / 10) // add 40% of InitSyncProgress as progress
+ c.ConversationListener().OnSyncServerProgress(c.progress) // notify server current Progress
+
+ syncWaitFunctions := []func(c context.Context) error{
+ c.IncrSyncConversations,
+ c.SyncAllConversationHashReadSeqs,
+ }
+ runSyncFunctions(ctx, syncWaitFunctions, syncWait)
+ log.ZWarn(ctx, "core data sync over", nil, "cost time", time.Since(c.startTime).Seconds())
+ c.addInitProgress(InitSyncProgress * 6 / 10) // add 60% of InitSyncProgress as progress
+ c.ConversationListener().OnSyncServerProgress(c.progress) // notify server current Progress
+
+ asyncNoWaitFunctions := []func(c context.Context) error{
+ c.user.SyncLoginUserInfoWithoutNotice,
+ c.relation.SyncAllBlackListWithoutNotice,
+ c.relation.SyncAllFriendApplicationWithoutNotice,
+ c.relation.SyncAllSelfFriendApplicationWithoutNotice,
+ c.group.SyncAllAdminGroupApplicationWithoutNotice,
+ c.group.SyncAllSelfGroupApplicationWithoutNotice,
+ c.user.SyncAllCommandWithoutNotice,
+ }
+ runSyncFunctions(ctx, asyncNoWaitFunctions, asyncNoWait)
+
+ case constant.AppDataSyncFinish:
+ log.ZDebug(ctx, "AppDataSyncFinish", "time", time.Since(c.startTime).Milliseconds())
+ c.progress = 100
+ c.ConversationListener().OnSyncServerProgress(c.progress)
+ c.ConversationListener().OnSyncServerFinish(true)
+ case constant.MsgSyncBegin:
+ log.ZDebug(ctx, "MsgSyncBegin")
+ c.ConversationListener().OnSyncServerStart(false)
+ c.syncData(c2v)
+ case constant.MsgSyncFailed:
+ c.ConversationListener().OnSyncServerFailed(false)
+ case constant.MsgSyncEnd:
+ log.ZDebug(ctx, "MsgSyncEnd", "time", time.Since(c.startTime).Milliseconds())
+ c.ConversationListener().OnSyncServerFinish(false)
+ }
+}
+
+func (c *Conversation) doNotificationManager(c2v common.Cmd2Value) {
+ ctx := c2v.Ctx
+ allMsg := c2v.Value.(sdk_struct.CmdNewMsgComeToConversation).Msgs
+
+ for conversationID, msgs := range allMsg {
+ log.ZDebug(ctx, "notification handling", "conversationID", conversationID, "msgs", msgs)
+
+ // First, process all the notifications
+ for _, msg := range msgs.Msgs {
+ if msg.ContentType > constant.FriendNotificationBegin && msg.ContentType < constant.FriendNotificationEnd {
+ c.relation.DoNotification(ctx, msg)
+ } else if msg.ContentType > constant.UserNotificationBegin && msg.ContentType < constant.UserNotificationEnd {
+ c.user.DoNotification(ctx, msg)
+ } else if msg.ContentType > constant.GroupNotificationBegin && msg.ContentType < constant.GroupNotificationEnd {
+ c.group.DoNotification(ctx, msg)
+ } else {
+ c.DoNotification(ctx, msg)
+ }
+ }
+
+ // After all notifications are processed, update the sequence number
+ if len(msgs.Msgs) != 0 {
+ lastMsg := msgs.Msgs[len(msgs.Msgs)-1]
+ log.ZDebug(ctx, "SetNotificationSeq", "conversationID", conversationID, "seq", lastMsg.Seq)
+ if lastMsg.Seq != 0 {
+ if err := c.db.SetNotificationSeq(ctx, conversationID, lastMsg.Seq); err != nil {
+ // Log an error if setting the sequence number fails
+ log.ZError(ctx, "SetNotificationSeq err", err, "conversationID", conversationID, "lastMsg", lastMsg)
+ }
+ }
+ }
+ }
+
+}
+
+func (c *Conversation) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
+ go func() {
+ if err := c.doNotification(ctx, msg); err != nil {
+ log.ZWarn(ctx, "DoConversationNotification failed", err)
+ }
+ }()
+}
+
+func (c *Conversation) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ switch msg.ContentType {
+ case constant.ConversationChangeNotification:
+ return c.DoConversationChangedNotification(ctx, msg)
+ case constant.ConversationPrivateChatNotification: // 1701
+ return c.DoConversationIsPrivateChangedNotification(ctx, msg)
+ case constant.BusinessNotification:
+ return c.doBusinessNotification(ctx, msg)
+ case constant.RevokeNotification: // 2101
+ return c.doRevokeMsg(ctx, msg)
+ case constant.ClearConversationNotification: // 1703
+ return c.doClearConversations(ctx, msg)
+ case constant.DeleteMsgsNotification:
+ return c.doDeleteMsgs(ctx, msg)
+ case constant.HasReadReceipt: // 2200
+ return c.doReadDrawing(ctx, msg)
+ case pconstant.StreamMsgNotification:
+ return c.doStreamMsgNotification(ctx, msg)
+ }
+ return errs.New("unknown tips type", "contentType", msg.ContentType).Wrap()
+}
+
+func (c *Conversation) getConversationLatestMsgClientID(latestMsg string) string {
+ msg := &sdk_struct.MsgStruct{}
+ if err := json.Unmarshal([]byte(latestMsg), msg); err != nil {
+ log.ZError(context.Background(), "getConversationLatestMsgClientID", err, "latestMsg", latestMsg)
+ }
+ return msg.ClientMsgID
+}
+
+func (c *Conversation) doUpdateConversation(c2v common.Cmd2Value) {
+ if c2v.Caller == "" {
+ c2v.Caller = common.GetCaller(2)
+ }
+ ctx := c2v.Ctx
+ node := c2v.Value.(common.UpdateConNode)
+ log.ZInfo(ctx, "doUpdateConversation", "node", node, "cmd", c2v.Cmd, "caller", c2v.Caller)
+ switch node.Action {
+ case constant.AddConOrUpLatMsg:
+ var list []*model_struct.LocalConversation
+ lc := node.Args.(model_struct.LocalConversation)
+ oc, err := c.db.GetConversation(ctx, lc.ConversationID)
+ if err == nil {
+ if lc.LatestMsgSendTime >= oc.LatestMsgSendTime || c.getConversationLatestMsgClientID(lc.LatestMsg) == c.getConversationLatestMsgClientID(oc.LatestMsg) { // The session update of asynchronous messages is subject to the latest sending time
+ err := c.db.UpdateColumnsConversation(ctx, node.ConID, map[string]interface{}{"latest_msg_send_time": lc.LatestMsgSendTime, "latest_msg": lc.LatestMsg})
+ if err != nil {
+ log.ZError(ctx, "updateConversationLatestMsgModel", err, "conversationID", node.ConID)
+ } else {
+ oc.LatestMsgSendTime = lc.LatestMsgSendTime
+ oc.LatestMsg = lc.LatestMsg
+ list = append(list, oc)
+ data := utils.StructToJsonString(list)
+ log.ZInfo(ctx, "OnConversationChanged", "data", data)
+ c.ConversationListener().OnConversationChanged(data)
+ }
+ }
+ } else {
+ log.ZDebug(ctx, "new conversation", "lc", lc)
+ err4 := c.db.InsertConversation(ctx, &lc)
+ if err4 != nil {
+ log.ZWarn(ctx, "insert new conversation err", err4)
+ } else {
+ list = append(list, &lc)
+ c.ConversationListener().OnNewConversation(utils.StructToJsonString(list))
+ }
+ }
+
+ case constant.TotalUnreadMessageChanged:
+ totalUnreadCount, err := c.db.GetTotalUnreadMsgCountDB(ctx)
+ if err != nil {
+ log.ZWarn(ctx, "GetTotalUnreadMsgCountDB err", err)
+ } else {
+ c.ConversationListener().OnTotalUnreadMessageCountChanged(totalUnreadCount)
+ }
+ case constant.UpdateConFaceUrlAndNickName:
+ var lc model_struct.LocalConversation
+ st := node.Args.(common.SourceIDAndSessionType)
+ log.ZInfo(ctx, "UpdateConFaceUrlAndNickName", "st", st)
+ switch st.SessionType {
+ case constant.SingleChatType:
+ lc.UserID = st.SourceID
+ lc.ConversationID = c.getConversationIDBySessionType(st.SourceID, constant.SingleChatType)
+ lc.ConversationType = constant.SingleChatType
+ case constant.ReadGroupChatType:
+ conversationID, conversationType, err := c.getConversationTypeByGroupID(ctx, st.SourceID)
+ if err != nil {
+ return
+ }
+ lc.GroupID = st.SourceID
+ lc.ConversationID = conversationID
+ lc.ConversationType = conversationType
+ case constant.NotificationChatType:
+ lc.UserID = st.SourceID
+ lc.ConversationID = c.getConversationIDBySessionType(st.SourceID, constant.NotificationChatType)
+ lc.ConversationType = constant.NotificationChatType
+ default:
+ log.ZError(ctx, "not support sessionType", nil, "sessionType", st.SessionType)
+ return
+ }
+ lc.ShowName = st.Nickname
+ lc.FaceURL = st.FaceURL
+ err := c.db.UpdateConversation(ctx, &lc)
+ if err != nil {
+ // log.Error("internal", "setConversationFaceUrlAndNickName database err:", err.Error())
+ return
+ }
+ c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: lc.ConversationID, Action: constant.ConChange, Args: []string{lc.ConversationID}}})
+
+ case constant.UpdateLatestMessageReadState:
+ conversationID := node.ConID
+ var latestMsg sdk_struct.MsgStruct
+ l, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZError(ctx, "getConversationLatestMsgModel err", err, "conversationID", conversationID)
+ } else {
+ err := json.Unmarshal([]byte(l.LatestMsg), &latestMsg)
+ if err != nil {
+ log.ZError(ctx, "latestMsg,Unmarshal err", err)
+ } else {
+ latestMsg.IsRead = true
+ newLatestMessage := utils.StructToJsonString(latestMsg)
+ err = c.db.UpdateColumnsConversation(ctx, node.ConID, map[string]interface{}{"latest_msg_send_time": latestMsg.SendTime, "latest_msg": newLatestMessage})
+ if err != nil {
+ log.ZError(ctx, "updateConversationLatestMsgModel err", err)
+ }
+ }
+ }
+ case constant.UpdateLatestMessageFaceUrlAndNickName:
+ args := node.Args.(common.UpdateMessageInfo)
+ switch args.SessionType {
+ case constant.ReadGroupChatType:
+ conversationID := c.getConversationIDBySessionType(args.GroupID, constant.ReadGroupChatType)
+ lc, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ log.ZWarn(ctx, "getConversation err", err)
+ return
+ }
+ var latestMsg sdk_struct.MsgStruct
+ err = json.Unmarshal([]byte(lc.LatestMsg), &latestMsg)
+ if err != nil {
+ log.ZError(ctx, "latestMsg,Unmarshal err", err)
+ } else {
+ //If the sender of the latest message in the conversation
+ //happens to be a member of the group whose status has changed,
+ //then update the sender's avatar and nickname for the latest message.
+ if latestMsg.SendID == args.UserID {
+ latestMsg.SenderFaceURL = args.FaceURL
+ latestMsg.SenderNickname = args.Nickname
+ newLatestMessage := utils.StructToJsonString(latestMsg)
+ lc.LatestMsg = newLatestMessage
+ err = c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"latest_msg": newLatestMessage})
+ if err != nil {
+ log.ZError(ctx, "updateConversationLatestMsgModel err", err)
+ } else {
+ var cList []*model_struct.LocalConversation
+ cList = append(cList, lc)
+ data := utils.StructToJsonStringDefault(cList)
+ c.ConversationListener().OnConversationChanged(data)
+ }
+
+ }
+ }
+ }
+
+ case constant.ConChange:
+ conversationIDs := node.Args.([]string)
+ conversations, err := c.db.GetMultipleConversationDB(ctx, conversationIDs)
+ if err != nil {
+ log.ZError(ctx, "getMultipleConversationModel err", err)
+ } else {
+ var newCList []*model_struct.LocalConversation
+ for _, v := range conversations {
+ if v.LatestMsgSendTime != 0 {
+ newCList = append(newCList, v)
+ }
+ }
+ data := utils.StructToJsonStringDefault(newCList)
+ log.ZInfo(ctx, "OnConversationChanged", "data", data)
+ c.ConversationListener().OnConversationChanged(data)
+ }
+ case constant.NewCon:
+ cidList := node.Args.([]string)
+ cLists, err := c.db.GetMultipleConversationDB(ctx, cidList)
+ if err != nil {
+ log.ZWarn(ctx, "getMultipleConversationModel err", err)
+ } else {
+ if cLists != nil {
+ log.ZDebug(ctx, "getMultipleConversationModel success", "cLists", cLists)
+ c.ConversationListener().OnNewConversation(utils.StructToJsonString(cLists))
+ }
+ }
+ case constant.ConChangeDirect:
+ cidList := node.Args.(string)
+ log.ZInfo(ctx, "ConversationChanged", "cidList", cidList)
+ c.ConversationListener().OnConversationChanged(cidList)
+
+ case constant.NewConDirect:
+ cidList := node.Args.(string)
+ log.ZDebug(ctx, "NewConversation", "cidList", cidList)
+ c.ConversationListener().OnNewConversation(cidList)
+
+ }
+}
+
+func (c *Conversation) doUpdateMessage(c2v common.Cmd2Value) {
+ node := c2v.Value.(common.UpdateMessageNode)
+ ctx := c2v.Ctx
+ switch node.Action {
+ case constant.UpdateMsgFaceUrlAndNickName:
+ args := node.Args.(common.UpdateMessageInfo)
+ switch args.SessionType {
+ case constant.SingleChatType:
+ if args.UserID == c.loginUserID {
+ conversationIDList, err := c.db.GetAllSingleConversationIDList(ctx)
+ if err != nil {
+ log.ZError(ctx, "GetAllSingleConversationIDList err", err)
+ return
+ } else {
+ log.ZDebug(ctx, "get single conversationID list", "conversationIDList", conversationIDList)
+ for _, conversationID := range conversationIDList {
+ err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
+ if err != nil {
+ log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
+ continue
+ }
+ }
+
+ }
+ } else {
+ conversationID := c.getConversationIDBySessionType(args.UserID, constant.SingleChatType)
+ err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
+ if err != nil {
+ log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
+ }
+
+ }
+ case constant.ReadGroupChatType:
+ conversationID := c.getConversationIDBySessionType(args.GroupID, constant.ReadGroupChatType)
+ err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
+ if err != nil {
+ log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
+ }
+ case constant.NotificationChatType:
+ conversationID := c.getConversationIDBySessionType(args.UserID, constant.NotificationChatType)
+ err := c.db.UpdateMsgSenderFaceURLAndSenderNickname(ctx, conversationID, args.UserID, args.FaceURL, args.Nickname)
+ if err != nil {
+ log.ZError(ctx, "UpdateMsgSenderFaceURLAndSenderNickname err", err)
+ }
+ default:
+ log.ZError(ctx, "not support sessionType", nil, "args", args)
+ return
+ }
+ }
+
+}
+
+func (c *Conversation) syncData(c2v common.Cmd2Value) {
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
+
+ ctx := c2v.Ctx
+ c.startTime = time.Now()
+ //clear SubscriptionStatusMap
+ //c.user.OnlineStatusCache.DeleteAll()
+
+ // Synchronous sync functions
+ syncFuncs := []func(c context.Context) error{
+ c.SyncAllConversationHashReadSeqs,
+ }
+
+ runSyncFunctions(ctx, syncFuncs, syncWait)
+
+ // Asynchronous sync functions
+ asyncFuncs := []func(c context.Context) error{
+ c.user.SyncLoginUserInfo,
+ c.relation.SyncAllBlackList,
+ c.relation.SyncAllFriendApplication,
+ c.relation.SyncAllSelfFriendApplication,
+ c.group.SyncAllAdminGroupApplication,
+ c.group.SyncAllSelfGroupApplication,
+ c.user.SyncAllCommand,
+ c.group.SyncAllJoinedGroupsAndMembers,
+ c.relation.IncrSyncFriends,
+ c.IncrSyncConversations,
+ }
+
+ runSyncFunctions(ctx, asyncFuncs, asyncNoWait)
+}
+
+func runSyncFunctions(ctx context.Context, funcs []func(c context.Context) error, mode int) {
+ var wg sync.WaitGroup
+
+ for _, fn := range funcs {
+ switch mode {
+ case asyncWait:
+ wg.Add(1)
+ go executeSyncFunction(ctx, fn, &wg)
+ case asyncNoWait:
+ go executeSyncFunction(ctx, fn, nil)
+ case syncWait:
+ executeSyncFunction(ctx, fn, nil)
+ }
+ }
+
+ if mode == asyncWait {
+ wg.Wait()
+ }
+}
+
+func executeSyncFunction(ctx context.Context, fn func(c context.Context) error, wg *sync.WaitGroup) {
+ if wg != nil {
+ defer wg.Done()
+ }
+
+ funcName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
+ startTime := time.Now()
+ err := fn(ctx)
+ duration := time.Since(startTime)
+ if err != nil {
+ log.ZWarn(ctx, fmt.Sprintf("%s sync error", funcName), err, "duration", duration.Seconds())
+ } else {
+ log.ZDebug(ctx, fmt.Sprintf("%s completed successfully", funcName), "duration", duration.Seconds())
+ }
+}
+
+func (c *Conversation) DoConversationChangedNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
+
+ //var notification sdkws.ConversationChangedNotification
+ tips := &sdkws.ConversationUpdateTips{}
+ if err := utils.UnmarshalNotificationElem(msg.Content, tips); err != nil {
+ log.ZWarn(ctx, "UnmarshalNotificationElem err", err, "msg", msg)
+ return err
+ }
+
+ err := c.IncrSyncConversations(ctx)
+ if err != nil {
+ log.ZWarn(ctx, "IncrSyncConversations err", err)
+ return err
+ }
+ return nil
+}
+
+func (c *Conversation) DoConversationIsPrivateChangedNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
+
+ tips := &sdkws.ConversationSetPrivateTips{}
+ if err := utils.UnmarshalNotificationElem(msg.Content, tips); err != nil {
+ log.ZWarn(ctx, "UnmarshalNotificationElem err", err, "msg", msg)
+ return err
+ }
+
+ err := c.IncrSyncConversations(ctx)
+ if err != nil {
+ log.ZWarn(ctx, "IncrSyncConversations err", err)
+ return err
+ }
+ return nil
+}
+
+func (c *Conversation) doBusinessNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ var n sdk_struct.NotificationElem
+ err := utils.JsonStringToStruct(string(msg.Content), &n)
+ if err != nil {
+ log.ZError(ctx, "unmarshal failed", err, "msg", msg)
+ return err
+
+ }
+ c.businessListener().OnRecvCustomBusinessMessage(n.Detail)
+ return nil
+}
diff --git a/internal/conversation_msg/progress.go b/internal/conversation_msg/progress.go
index c19798231..430635324 100644
--- a/internal/conversation_msg/progress.go
+++ b/internal/conversation_msg/progress.go
@@ -17,7 +17,8 @@ package conversation_msg
import (
"context"
"encoding/json"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
+
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
diff --git a/internal/conversation_msg/read_drawing.go b/internal/conversation_msg/read_drawing.go
index 9ebc16932..36c675c22 100644
--- a/internal/conversation_msg/read_drawing.go
+++ b/internal/conversation_msg/read_drawing.go
@@ -18,35 +18,19 @@ import (
"context"
"encoding/json"
"errors"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/utils/datautil"
- pbMsg "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
)
-func (c *Conversation) markMsgAsRead2Svr(ctx context.Context, conversationID string, seqs []int64) error {
- req := &pbMsg.MarkMsgsAsReadReq{UserID: c.loginUserID, ConversationID: conversationID, Seqs: seqs}
- return util.ApiPost(ctx, constant.MarkMsgsAsReadRouter, req, nil)
-}
-
-func (c *Conversation) markConversationAsReadSvr(ctx context.Context, conversationID string, hasReadSeq int64, seqs []int64) error {
- req := &pbMsg.MarkConversationAsReadReq{UserID: c.loginUserID, ConversationID: conversationID, HasReadSeq: hasReadSeq, Seqs: seqs}
- return util.ApiPost(ctx, constant.MarkConversationAsRead, req, nil)
-}
-
-func (c *Conversation) setConversationHasReadSeq(ctx context.Context, conversationID string, hasReadSeq int64) error {
- req := &pbMsg.SetConversationHasReadSeqReq{UserID: c.loginUserID, ConversationID: conversationID, HasReadSeq: hasReadSeq}
- return util.ApiPost(ctx, constant.SetConversationHasReadSeq, req, nil)
-}
-
func (c *Conversation) getConversationMaxSeqAndSetHasRead(ctx context.Context, conversationID string) error {
maxSeq, err := c.db.GetConversationNormalMsgSeq(ctx, conversationID)
if err != nil {
@@ -55,23 +39,20 @@ func (c *Conversation) getConversationMaxSeqAndSetHasRead(ctx context.Context, c
if maxSeq == 0 {
return nil
}
- if err := c.setConversationHasReadSeq(ctx, conversationID, maxSeq); err != nil {
- return err
- }
- if err := c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"has_read_seq": maxSeq}); err != nil {
- return err
- }
- return nil
+ return c.setConversationHasReadSeq(ctx, conversationID, maxSeq)
}
// mark a conversation's all message as read
func (c *Conversation) markConversationMessageAsRead(ctx context.Context, conversationID string) error {
+ c.conversationSyncMutex.Lock()
+ defer c.conversationSyncMutex.Unlock()
conversation, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
if conversation.UnreadCount == 0 {
- return sdkerrs.ErrUnreadCount
+ log.ZWarn(ctx, "unread count is 0", nil, "conversationID", conversationID)
+ return nil
}
// get the maximum sequence number of messages in the table that are not sent by oneself
peerUserMaxSeq, err := c.db.GetConversationPeerNormalMsgSeq(ctx, conversationID)
@@ -93,20 +74,23 @@ func (c *Conversation) markConversationMessageAsRead(ctx context.Context, conver
msgIDs, seqs := c.getAsReadMsgMapAndList(ctx, msgs)
if len(seqs) == 0 {
log.ZWarn(ctx, "seqs is empty", nil, "conversationID", conversationID)
- return nil
- }
- log.ZDebug(ctx, "markConversationMessageAsRead", "conversationID", conversationID, "seqs",
- seqs, "peerUserMaxSeq", peerUserMaxSeq, "maxSeq", maxSeq)
- if err := c.markConversationAsReadSvr(ctx, conversationID, maxSeq, seqs); err != nil {
- return err
- }
- _, err = c.db.MarkConversationMessageAsReadDB(ctx, conversationID, msgIDs)
- if err != nil {
- log.ZWarn(ctx, "MarkConversationMessageAsRead err", err, "conversationID", conversationID, "msgIDs", msgIDs)
+ if err := c.markConversationAsReadServer(ctx, conversationID, maxSeq, seqs); err != nil {
+ return err
+ }
+ } else {
+ log.ZDebug(ctx, "markConversationMessageAsRead", "conversationID", conversationID, "seqs",
+ seqs, "peerUserMaxSeq", peerUserMaxSeq, "maxSeq", maxSeq)
+ if err := c.markConversationAsReadServer(ctx, conversationID, maxSeq, seqs); err != nil {
+ return err
+ }
+ _, err = c.db.MarkConversationMessageAsReadDB(ctx, conversationID, msgIDs)
+ if err != nil {
+ log.ZWarn(ctx, "MarkConversationMessageAsRead err", err, "conversationID", conversationID, "msgIDs", msgIDs)
+ }
}
- case constant.SuperGroupChatType, constant.NotificationChatType:
+ case constant.ReadGroupChatType, constant.NotificationChatType:
log.ZDebug(ctx, "markConversationMessageAsRead", "conversationID", conversationID, "peerUserMaxSeq", peerUserMaxSeq, "maxSeq", maxSeq)
- if err := c.markConversationAsReadSvr(ctx, conversationID, maxSeq, nil); err != nil {
+ if err := c.markConversationAsReadServer(ctx, conversationID, maxSeq, nil); err != nil {
return err
}
}
@@ -143,7 +127,7 @@ func (c *Conversation) markMessagesAsReadByMsgID(ctx context.Context, conversati
log.ZWarn(ctx, "seqs is empty", nil, "conversationID", conversationID)
return nil
}
- if err := c.markMsgAsRead2Svr(ctx, conversationID, seqs); err != nil {
+ if err := c.markMsgAsRead2Server(ctx, conversationID, seqs); err != nil {
return err
}
decrCount, err := c.db.MarkConversationMessageAsReadDB(ctx, conversationID, markAsReadMsgIDs)
@@ -178,7 +162,7 @@ func (c *Conversation) getAsReadMsgMapAndList(ctx context.Context,
func (c *Conversation) unreadChangeTrigger(ctx context.Context, conversationID string, latestMsgIsRead bool) {
if latestMsgIsRead {
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversationID,
- Action: constant.UpdateLatestMessageChange, Args: []string{conversationID}}, Ctx: ctx})
+ Action: constant.UpdateLatestMessageReadState, Args: []string{conversationID}}, Ctx: ctx})
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversationID,
Action: constant.ConChange, Args: []string{conversationID}}, Ctx: ctx})
@@ -186,70 +170,89 @@ func (c *Conversation) unreadChangeTrigger(ctx context.Context, conversationID s
Ctx: ctx})
}
-func (c *Conversation) doUnreadCount(ctx context.Context, conversation *model_struct.LocalConversation, hasReadSeq int64, seqs []int64) {
+func (c *Conversation) doUnreadCount(ctx context.Context, conversation *model_struct.LocalConversation, hasReadSeq int64, seqs []int64) error {
if conversation.ConversationType == constant.SingleChatType {
if len(seqs) != 0 {
- _, err := c.db.MarkConversationMessageAsReadBySeqs(ctx, conversation.ConversationID, seqs)
+ hasReadMessage, err := c.db.GetMessageBySeq(ctx, conversation.ConversationID, hasReadSeq)
if err != nil {
- log.ZWarn(ctx, "MarkConversationMessageAsReadBySeqs err", err, "conversationID", conversation.ConversationID, "seqs", seqs)
+ return err
}
+ if hasReadMessage.IsRead {
+ return errs.New("read info from self can be ignored").Wrap()
+
+ } else {
+ _, err := c.db.MarkConversationMessageAsReadBySeqs(ctx, conversation.ConversationID, seqs)
+ if err != nil {
+ return err
+ }
+ }
+
} else {
- log.ZWarn(ctx, "seqs is empty", nil, "conversationID", conversation.ConversationID, "hasReadSeq", hasReadSeq)
+ return errs.New("seqList is empty", "conversationID", conversation.ConversationID, "hasReadSeq", hasReadSeq).Wrap()
}
- if hasReadSeq > conversation.HasReadSeq {
- decrUnreadCount := hasReadSeq - conversation.HasReadSeq
- if err := c.db.DecrConversationUnreadCount(ctx, conversation.ConversationID, decrUnreadCount); err != nil {
- log.ZError(ctx, "DecrConversationUnreadCount err", err, "conversationID", conversation.ConversationID, "decrUnreadCount", decrUnreadCount)
+ currentMaxSeq := c.maxSeqRecorder.Get(conversation.ConversationID)
+ if currentMaxSeq == 0 {
+ return errs.New("currentMaxSeq is 0", "conversationID", conversation.ConversationID).Wrap()
+ } else {
+ unreadCount := currentMaxSeq - hasReadSeq
+ if unreadCount < 0 {
+ log.ZWarn(ctx, "unread count is less than 0", nil, "conversationID", conversation.ConversationID, "currentMaxSeq", currentMaxSeq, "hasReadSeq", hasReadSeq)
+ unreadCount = 0
}
- if err := c.db.UpdateColumnsConversation(ctx, conversation.ConversationID, map[string]interface{}{"has_read_seq": hasReadSeq}); err != nil {
- log.ZError(ctx, "UpdateColumnsConversation err", err, "conversationID", conversation.ConversationID)
+ if err := c.db.UpdateColumnsConversation(ctx, conversation.ConversationID, map[string]interface{}{"unread_count": unreadCount}); err != nil {
+ return err
}
}
latestMsg := &sdk_struct.MsgStruct{}
if err := json.Unmarshal([]byte(conversation.LatestMsg), latestMsg); err != nil {
log.ZError(ctx, "Unmarshal err", err, "conversationID", conversation.ConversationID, "latestMsg", conversation.LatestMsg)
+ return err
}
if (!latestMsg.IsRead) && datautil.Contain(latestMsg.Seq, seqs...) {
- latestMsg.IsRead = true
- conversation.LatestMsg = utils.StructToJsonString(&latestMsg)
- _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversation.ConversationID, Action: constant.AddConOrUpLatMsg, Args: *conversation}, c.GetCh())
+ c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversation.ConversationID,
+ Action: constant.UpdateLatestMessageReadState, Args: []string{conversation.ConversationID}}, Ctx: ctx})
}
} else {
if err := c.db.UpdateColumnsConversation(ctx, conversation.ConversationID, map[string]interface{}{"unread_count": 0}); err != nil {
log.ZError(ctx, "UpdateColumnsConversation err", err, "conversationID", conversation.ConversationID)
+ return err
}
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversation.ConversationID, Action: constant.ConChange, Args: []string{conversation.ConversationID}}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
+ return nil
}
-func (c *Conversation) doReadDrawing(ctx context.Context, msg *sdkws.MsgData) {
+func (c *Conversation) doReadDrawing(ctx context.Context, msg *sdkws.MsgData) error {
tips := &sdkws.MarkAsReadTips{}
err := utils.UnmarshalNotificationElem(msg.Content, tips)
if err != nil {
log.ZWarn(ctx, "UnmarshalNotificationElem err", err, "msg", msg)
- return
+ return err
}
log.ZDebug(ctx, "do readDrawing", "tips", tips)
conversation, err := c.db.GetConversation(ctx, tips.ConversationID)
if err != nil {
- log.ZError(ctx, "GetConversation err", err, "conversationID", tips.ConversationID)
- return
+ log.ZWarn(ctx, "GetConversation err", err, "conversationID", tips.ConversationID)
+ return err
+
}
if tips.MarkAsReadUserID != c.loginUserID {
if len(tips.Seqs) == 0 {
- return
+ return errs.New("tips Seqs is empty").Wrap()
}
messages, err := c.db.GetMessagesBySeqs(ctx, tips.ConversationID, tips.Seqs)
if err != nil {
- log.ZError(ctx, "GetMessagesBySeqs err", err, "conversationID", tips.ConversationID, "seqs", tips.Seqs)
- return
+ log.ZWarn(ctx, "GetMessagesBySeqs err", err, "conversationID", tips.ConversationID, "seqs", tips.Seqs)
+ return err
+
}
if conversation.ConversationType == constant.SingleChatType {
latestMsg := &sdk_struct.MsgStruct{}
if err := json.Unmarshal([]byte(conversation.LatestMsg), latestMsg); err != nil {
- log.ZError(ctx, "Unmarshal err", err, "conversationID", tips.ConversationID, "latestMsg", conversation.LatestMsg)
+ log.ZWarn(ctx, "Unmarshal err", err, "conversationID", tips.ConversationID, "latestMsg", conversation.LatestMsg)
+ return err
}
var successMsgIDs []string
for _, message := range messages {
@@ -259,7 +262,8 @@ func (c *Conversation) doReadDrawing(ctx context.Context, msg *sdkws.MsgData) {
message.AttachedInfo = utils.StructToJsonString(attachInfo)
message.IsRead = true
if err = c.db.UpdateMessage(ctx, tips.ConversationID, message); err != nil {
- log.ZError(ctx, "UpdateMessage err", err, "conversationID", tips.ConversationID, "message", message)
+ log.ZWarn(ctx, "UpdateMessage err", err, "conversationID", tips.ConversationID, "message", message)
+ return err
} else {
if latestMsg.ClientMsgID == message.ClientMsgID {
latestMsg.IsRead = message.IsRead
@@ -275,6 +279,7 @@ func (c *Conversation) doReadDrawing(ctx context.Context, msg *sdkws.MsgData) {
c.msgListener().OnRecvC2CReadReceipt(utils.StructToJsonString(messageReceiptResp))
}
} else {
- c.doUnreadCount(ctx, conversation, tips.HasReadSeq, tips.Seqs)
+ return c.doUnreadCount(ctx, conversation, tips.HasReadSeq, tips.Seqs)
}
+ return nil
}
diff --git a/internal/conversation_msg/revoke.go b/internal/conversation_msg/revoke.go
index d2743a394..4ed6163fc 100644
--- a/internal/conversation_msg/revoke.go
+++ b/internal/conversation_msg/revoke.go
@@ -17,55 +17,60 @@ package conversation_msg
import (
"context"
"errors"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
+ "time"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/utils/timeutil"
- "github.com/jinzhu/copier"
- pbMsg "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
)
-func (c *Conversation) doRevokeMsg(ctx context.Context, msg *sdkws.MsgData) {
+func (c *Conversation) doRevokeMsg(ctx context.Context, msg *sdkws.MsgData) error {
var tips sdkws.RevokeMsgTips
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
- log.ZError(ctx, "unmarshal failed", err, "msg", msg)
- return
+ log.ZWarn(ctx, "unmarshal failed", err, "msg", msg)
+ return errs.Wrap(err)
}
log.ZDebug(ctx, "do revokeMessage", "tips", &tips)
- c.revokeMessage(ctx, &tips)
+ return c.revokeMessage(ctx, &tips)
}
-func (c *Conversation) revokeMessage(ctx context.Context, tips *sdkws.RevokeMsgTips) {
+func (c *Conversation) revokeMessage(ctx context.Context, tips *sdkws.RevokeMsgTips) error {
revokedMsg, err := c.db.GetMessageBySeq(ctx, tips.ConversationID, tips.Seq)
if err != nil {
log.ZError(ctx, "GetMessageBySeq failed", err, "tips", &tips)
- return
+ return errs.Wrap(err)
}
+
var revokerRole int32
var revokerNickname string
if tips.IsAdminRevoke || tips.SesstionType == constant.SingleChatType {
_, userName, err := c.getUserNameAndFaceURL(ctx, tips.RevokerUserID)
if err != nil {
log.ZError(ctx, "GetUserNameAndFaceURL failed", err, "tips", &tips)
+ return errs.Wrap(err)
} else {
log.ZDebug(ctx, "revoker user name", "userName", userName)
}
+
revokerNickname = userName
- } else if tips.SesstionType == constant.SuperGroupChatType {
+ } else if tips.SesstionType == constant.ReadGroupChatType {
conversation, err := c.db.GetConversation(ctx, tips.ConversationID)
if err != nil {
log.ZError(ctx, "GetConversation failed", err, "conversationID", tips.ConversationID)
- return
+ return errs.Wrap(err)
}
+
groupMember, err := c.db.GetGroupMemberInfoByGroupIDUserID(ctx, conversation.GroupID, tips.RevokerUserID)
if err != nil {
log.ZError(ctx, "GetGroupMemberInfoByGroupIDUserID failed", err, "tips", &tips)
+ return errs.Wrap(err)
} else {
log.ZDebug(ctx, "revoker member name", "groupMember", groupMember)
revokerRole = groupMember.RoleLevel
@@ -92,65 +97,97 @@ func (c *Conversation) revokeMessage(ctx context.Context, tips *sdkws.RevokeMsgT
if err := c.db.UpdateMessageBySeq(ctx, tips.ConversationID, &model_struct.LocalChatLog{Seq: tips.Seq,
Content: utils.StructToJsonString(n), ContentType: constant.RevokeNotification}); err != nil {
log.ZError(ctx, "UpdateMessageBySeq failed", err, "tips", &tips)
- return
+ return errs.Wrap(err)
}
conversation, err := c.db.GetConversation(ctx, tips.ConversationID)
if err != nil {
log.ZError(ctx, "GetConversation failed", err, "tips", &tips)
- return
+ return errs.Wrap(err)
}
var latestMsg sdk_struct.MsgStruct
utils.JsonStringToStruct(conversation.LatestMsg, &latestMsg)
log.ZDebug(ctx, "latestMsg", "latestMsg", &latestMsg, "seq", tips.Seq)
if latestMsg.Seq <= tips.Seq {
- var newLatesetMsg sdk_struct.MsgStruct
- msgs, err := c.db.GetMessageListNoTime(ctx, tips.ConversationID, 1, false)
+ var newLatestMsg sdk_struct.MsgStruct
+ msgs, err := c.db.GetMessageList(ctx, tips.ConversationID, 1, 0, 0, "", false)
if err != nil || len(msgs) == 0 {
log.ZError(ctx, "GetMessageListNoTime failed", err, "tips", &tips)
- return
+ return errs.Wrap(err)
}
log.ZDebug(ctx, "latestMsg is revoked", "seq", tips.Seq, "msg", msgs[0])
- copier.Copy(&newLatesetMsg, msgs[0])
- err = c.msgConvert(&newLatesetMsg)
- if err != nil {
- log.ZError(ctx, "parsing data error", err, latestMsg)
+ newLatestMsg = *LocalChatLogToMsgStruct(msgs[0])
+ log.ZDebug(ctx, "revoke update conversatoin", "msg", utils.StructToJsonString(newLatestMsg))
+ if err := c.db.UpdateColumnsConversation(ctx, tips.ConversationID, map[string]interface{}{"latest_msg": utils.StructToJsonString(newLatestMsg),
+ "latest_msg_send_time": newLatestMsg.SendTime}); err != nil {
+ log.ZError(ctx, "UpdateColumnsConversation failed", err, "newLatestMsg", newLatestMsg)
} else {
- log.ZDebug(ctx, "revoke update conversatoin", "msg", utils.StructToJsonString(newLatesetMsg))
- if err := c.db.UpdateColumnsConversation(ctx, tips.ConversationID, map[string]interface{}{"latest_msg": utils.StructToJsonString(newLatesetMsg),
- "latest_msg_send_time": newLatesetMsg.SendTime}); err != nil {
- log.ZError(ctx, "UpdateColumnsConversation failed", err, "newLatesetMsg", newLatesetMsg)
- } else {
- c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{tips.ConversationID}}})
- }
+ c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{tips.ConversationID}}})
}
+
}
c.msgListener().OnNewRecvMessageRevoked(utils.StructToJsonString(m))
msgList, err := c.db.SearchAllMessageByContentType(ctx, conversation.ConversationID, constant.Quote)
if err != nil {
log.ZError(ctx, "SearchAllMessageByContentType failed", err, "tips", &tips)
- return
+ return errs.Wrap(err)
}
+
for _, v := range msgList {
- c.quoteMsgRevokeHandle(ctx, tips.ConversationID, v, m)
+ err = c.quoteMsgRevokeHandle(ctx, tips.ConversationID, v, m)
+ if err != nil {
+ log.ZError(ctx, "quote Msg Revoke Handle failed.", err, "chat Log content", v)
+ }
}
+ return errs.Wrap(err)
}
-func (c *Conversation) quoteMsgRevokeHandle(ctx context.Context, conversationID string, v *model_struct.LocalChatLog, revokedMsg sdk_struct.MessageRevoked) {
- s := sdk_struct.MsgStruct{}
- _ = utils.JsonStringToStruct(v.Content, &s.QuoteElem)
+func (c *Conversation) quoteMsgRevokeHandle(ctx context.Context, conversationID string, v *model_struct.LocalChatLog, revokedMsg sdk_struct.MessageRevoked) error {
+ s := sdk_struct.QuoteElem{}
+ if v.Content == "" {
+ return errs.New("Chat Log Content not found")
+ }
- if s.QuoteElem.QuoteMessage == nil {
- return
+ if err := utils.JsonStringToStruct(v.Content, &s); err != nil {
+ return errs.New("ChatLog content transfer failed.")
}
- if s.QuoteElem.QuoteMessage.ClientMsgID != revokedMsg.ClientMsgID {
- return
+
+ if s.QuoteMessage == nil {
+ return errs.New("QuoteMessage is nil").Wrap()
+ }
+ if s.QuoteMessage.ClientMsgID != revokedMsg.ClientMsgID {
+ return nil
}
- s.QuoteElem.QuoteMessage.Content = utils.StructToJsonString(revokedMsg)
- s.QuoteElem.QuoteMessage.ContentType = constant.RevokeNotification
- v.Content = utils.StructToJsonString(s.QuoteElem)
+
+ s.QuoteMessage.Content = utils.StructToJsonString(revokedMsg)
+ s.QuoteMessage.ContentType = constant.RevokeNotification
+ v.Content = utils.StructToJsonString(s)
if err := c.db.UpdateMessageBySeq(ctx, conversationID, v); err != nil {
log.ZError(ctx, "UpdateMessage failed", err, "v", v)
+ return errs.Wrap(err)
}
+ return nil
+}
+
+func (c *Conversation) waitForMessageSyncSeq(ctx context.Context, conversationID, clientMsgID string) (*model_struct.LocalChatLog, error) {
+ maxRetries := 5
+ for retries := 0; retries < maxRetries; retries++ {
+ message, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
+ if err != nil {
+ return nil, err
+ }
+
+ if message.Seq == 0 {
+
+ log.ZInfo(ctx, "Message seq is 0, waiting for retry", "conversationID", conversationID, "clientMsgID", clientMsgID)
+ _ = common.TriggerCmdIMMessageSync(ctx, c.msgSyncerCh)
+ time.Sleep(2 * time.Second)
+ continue
+ }
+
+ return message, nil
+ }
+
+ return nil, errs.New("message.seq is still 0 after maximum retries").Wrap()
}
func (c *Conversation) revokeOneMessage(ctx context.Context, conversationID, clientMsgID string) error {
@@ -158,7 +195,7 @@ func (c *Conversation) revokeOneMessage(ctx context.Context, conversationID, cli
if err != nil {
return err
}
- message, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
+ message, err := c.waitForMessageSyncSeq(ctx, conversationID, clientMsgID)
if err != nil {
return err
}
@@ -170,7 +207,7 @@ func (c *Conversation) revokeOneMessage(ctx context.Context, conversationID, cli
if message.SendID != c.loginUserID {
return errors.New("only send by yourself message can be revoked")
}
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
if message.SendID != c.loginUserID {
groupAdmins, err := c.db.GetGroupMemberOwnerAndAdminDB(ctx, conversation.GroupID)
if err != nil {
@@ -188,9 +225,12 @@ func (c *Conversation) revokeOneMessage(ctx context.Context, conversationID, cli
}
}
}
- if err := util.ApiPost(ctx, constant.RevokeMsgRouter, pbMsg.RevokeMsgReq{ConversationID: conversationID, Seq: message.Seq, UserID: c.loginUserID}, nil); err != nil {
+
+ err = c.revokeMessageFromServer(ctx, conversationID, message.Seq)
+ if err != nil {
return err
}
+
c.revokeMessage(ctx, &sdkws.RevokeMsgTips{
ConversationID: conversationID,
Seq: message.Seq,
diff --git a/internal/conversation_msg/server_api.go b/internal/conversation_msg/server_api.go
new file mode 100644
index 000000000..a409e82d9
--- /dev/null
+++ b/internal/conversation_msg/server_api.go
@@ -0,0 +1,76 @@
+package conversation_msg
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ pbConversation "github.com/openimsdk/protocol/conversation"
+ pbMsg "github.com/openimsdk/protocol/msg"
+)
+
+func (c *Conversation) markMsgAsRead2Server(ctx context.Context, conversationID string, seqs []int64) error {
+ req := &pbMsg.MarkMsgsAsReadReq{UserID: c.loginUserID, ConversationID: conversationID, Seqs: seqs}
+ return api.MarkMsgsAsRead.Execute(ctx, req)
+}
+
+func (c *Conversation) markConversationAsReadServer(ctx context.Context, conversationID string, hasReadSeq int64, seqs []int64) error {
+ req := &pbMsg.MarkConversationAsReadReq{UserID: c.loginUserID, ConversationID: conversationID, HasReadSeq: hasReadSeq, Seqs: seqs}
+ return api.MarkConversationAsRead.Execute(ctx, req)
+}
+
+func (c *Conversation) setConversationHasReadSeq(ctx context.Context, conversationID string, hasReadSeq int64) error {
+ req := &pbMsg.SetConversationHasReadSeqReq{UserID: c.loginUserID, ConversationID: conversationID, HasReadSeq: hasReadSeq}
+ return api.SetConversationHasReadSeq.Execute(ctx, req)
+}
+
+// To delete session information, delete the server first, and then invoke the interface.
+// The client receives a callback to delete all local information.
+func (c *Conversation) clearConversationMsgFromServer(ctx context.Context, conversationID string) error {
+ req := &pbMsg.ClearConversationsMsgReq{UserID: c.loginUserID, ConversationIDs: []string{conversationID}}
+ return api.ClearConversationMsg.Execute(ctx, req)
+}
+
+// Delete all server messages
+func (c *Conversation) deleteAllMessageFromServer(ctx context.Context) error {
+ req := &pbMsg.UserClearAllMsgReq{UserID: c.loginUserID}
+ return api.ClearAllMsg.Execute(ctx, req)
+}
+
+// The user deletes part of the message from the server
+func (c *Conversation) deleteMessagesFromServer(ctx context.Context, conversationID string, seqs []int64) error {
+ req := &pbMsg.DeleteMsgsReq{UserID: c.loginUserID, Seqs: seqs, ConversationID: conversationID}
+ return api.DeleteMsgs.Execute(ctx, req)
+}
+
+func (c *Conversation) revokeMessageFromServer(ctx context.Context, conversationID string, seq int64) error {
+ req := &pbMsg.RevokeMsgReq{UserID: c.loginUserID, ConversationID: conversationID, Seq: seq}
+ return api.RevokeMsg.Execute(ctx, req)
+}
+
+func (c *Conversation) getHasReadAndMaxSeqsFromServer(ctx context.Context, conversationIDs ...string) (*pbMsg.GetConversationsHasReadAndMaxSeqResp, error) {
+ req := pbMsg.GetConversationsHasReadAndMaxSeqReq{UserID: c.loginUserID, ConversationIDs: conversationIDs}
+ return api.GetConversationsHasReadAndMaxSeq.Invoke(ctx, &req)
+}
+
+func (c *Conversation) getConversationsByIDsFromServer(ctx context.Context, conversations []string) (*pbConversation.GetConversationsResp, error) {
+ req := &pbConversation.GetConversationsReq{OwnerUserID: c.loginUserID, ConversationIDs: conversations}
+ return api.GetConversations.Invoke(ctx, req)
+}
+
+func (c *Conversation) getAllConversationListFromServer(ctx context.Context) (*pbConversation.GetAllConversationsResp, error) {
+ req := &pbConversation.GetAllConversationsReq{OwnerUserID: c.loginUserID}
+ return api.GetAllConversations.Invoke(ctx, req)
+}
+
+func (c *Conversation) getAllConversationIDsFromServer(ctx context.Context) (*pbConversation.GetFullOwnerConversationIDsResp, error) {
+ req := &pbConversation.GetFullOwnerConversationIDsReq{UserID: c.loginUserID}
+ return api.GetFullConversationIDs.Invoke(ctx, req)
+}
+
+func (c *Conversation) getIncrementalConversationFromServer(ctx context.Context, version uint64, versionID string) (*pbConversation.GetIncrementalConversationResp, error) {
+ req := &pbConversation.GetIncrementalConversationReq{UserID: c.loginUserID, Version: version, VersionID: versionID}
+ return api.GetIncrementalConversation.Invoke(ctx, req)
+}
+
+func (c *Conversation) getStreamMsg(ctx context.Context, clientMsgID string) (*pbMsg.GetStreamMsgResp, error) {
+ return api.GetStreamMsg.Invoke(ctx, &pbMsg.GetStreamMsgReq{ClientMsgID: clientMsgID})
+}
diff --git a/internal/conversation_msg/stream_msg.go b/internal/conversation_msg/stream_msg.go
new file mode 100644
index 000000000..538ff186a
--- /dev/null
+++ b/internal/conversation_msg/stream_msg.go
@@ -0,0 +1,173 @@
+package conversation_msg
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+)
+
+func (c *Conversation) parseStreamMsgTips(content []byte) (*sdkws.StreamMsgTips, error) {
+ var notificationElem sdkws.NotificationElem
+ if err := json.Unmarshal(content, ¬ificationElem); err != nil {
+ return nil, err
+ }
+ var tips sdkws.StreamMsgTips
+ if err := json.Unmarshal([]byte(notificationElem.Detail), &tips); err != nil {
+ return nil, err
+ }
+ return &tips, nil
+}
+
+func (c *Conversation) doStreamMsgNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ tips, err := c.parseStreamMsgTips(msg.Content)
+ if err != nil {
+ return err
+ }
+ dbMsg, err := c.db.GetMessage(ctx, tips.ConversationID, tips.ClientMsgID)
+ if err != nil {
+ log.ZWarn(ctx, "get db stream msg failed", err, "tips", tips)
+ return err
+ }
+ if dbMsg.ContentType != constant.Stream {
+ return errors.New("content type is not stream")
+ }
+ var streamElem sdk_struct.StreamElem
+ if err := json.Unmarshal([]byte(dbMsg.Content), &streamElem); err != nil {
+ return err
+ }
+ if streamElem.End {
+ log.ZWarn(ctx, "db stream msg is end", nil)
+ return nil
+ }
+ if len(streamElem.Packets) < int(tips.StartIndex) {
+ log.ZWarn(ctx, "db stream msg packets is not enough", nil, "streamElem", streamElem, "tips", tips)
+ c.asyncStreamMsg(ctx, tips.ConversationID, tips.ClientMsgID)
+ return nil
+ }
+ streamElem.Packets = streamElem.Packets[:tips.StartIndex]
+ for _, packet := range tips.Packets {
+ streamElem.Packets = append(streamElem.Packets, packet)
+ }
+ streamElem.End = tips.End
+ data := utils.StructToJsonString(streamElem)
+ if data == dbMsg.Content {
+ log.ZDebug(ctx, "stream msg unchanged")
+ return nil
+ }
+ dbMsg.Content = string(data)
+ return c.setStreamMsg(ctx, tips.ConversationID, dbMsg)
+}
+
+func (c *Conversation) setStreamMsg(ctx context.Context, conversationID string, msg *model_struct.LocalChatLog) error {
+ if err := c.db.UpdateMessage(ctx, conversationID, msg); err != nil {
+ return err
+ }
+ //_, res := c.LocalChatLog2MsgStruct(ctx, []*model_struct.LocalChatLog{msg})
+ //if len(res) == 0 {
+ // log.ZWarn(ctx, "LocalChatLog2MsgStruct failed", nil, "msg", msg)
+ // return nil
+ //}
+ //data := utils.StructToJsonString(res[0])
+ //log.ZDebug(ctx, "setStreamMsg", "data", data)
+ //c.msgListener().OnMsgEdited(data)
+ return c.updateConversationLastMsg(ctx, conversationID, msg)
+}
+
+func (c *Conversation) updateConversationLastMsg(ctx context.Context, conversationID string, msg *model_struct.LocalChatLog) error {
+ oc, err := c.db.GetConversation(ctx, conversationID)
+ if err != nil {
+ return err
+ }
+ if oc.LatestMsg != "" {
+ var conversationMsg model_struct.LocalChatLog
+ if err := json.Unmarshal([]byte(oc.LatestMsg), &conversationMsg); err != nil {
+ return err
+ }
+ if conversationMsg.SendTime >= msg.SendTime && conversationMsg.ClientMsgID != msg.ClientMsgID {
+ return nil
+ }
+ }
+ //_, res := c.LocalChatLog2MsgStruct(ctx, []*model_struct.LocalChatLog{msg})
+ //if len(res) == 0 {
+ // log.ZWarn(ctx, "LocalChatLog2MsgStruct failed", nil, "msg", msg)
+ // return nil
+ //}
+ oc.LatestMsgSendTime = msg.SendTime
+ //oc.LatestMsg = utils.StructToJsonString(res[0])
+ if err := c.db.UpdateConversation(ctx, oc); err != nil {
+ return err
+ }
+ conversationData := utils.StructToJsonString([]*model_struct.LocalConversation{oc})
+ log.ZDebug(ctx, "setStreamMsg conversation changed", "conversationData", conversationData)
+ c.ConversationListener().OnConversationChanged(conversationData)
+ return nil
+}
+
+func (c *Conversation) syncStreamMsg(ctx context.Context, conversationID string, clientMsgID string) error {
+ c.streamMsgMutex.Lock()
+ defer c.streamMsgMutex.Unlock()
+ ctx, cancel := context.WithTimeout(ctx, time.Second*5)
+ defer cancel()
+ dbMsg, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
+ if err != nil {
+ return err
+ }
+ if dbMsg.ContentType != constant.Stream {
+ return errors.New("content type is not stream")
+ }
+ var streamElem sdk_struct.StreamElem
+ if err := json.Unmarshal([]byte(dbMsg.Content), &streamElem); err != nil {
+ return errs.WrapMsg(err, "unmarshal stream msg failed")
+ }
+ if streamElem.End {
+ return nil
+ }
+ resp, err := c.getStreamMsg(ctx, clientMsgID)
+ if err != nil {
+ return err
+ }
+ streamElem.Packets = resp.Packets
+ streamElem.End = resp.End
+ content := utils.StructToJsonString(&streamElem)
+ if dbMsg.Content == content {
+ log.ZDebug(ctx, "stream msg unchanged", "conversationID", conversationID, "clientMsgID", clientMsgID, "streamElem", streamElem)
+ return nil
+ }
+ dbMsg.Content = content
+ return c.setStreamMsg(ctx, conversationID, dbMsg)
+}
+
+func (c *Conversation) asyncStreamMsg(ctx context.Context, conversationID string, clientMsgID string) {
+ ctx = context.WithoutCancel(ctx)
+ go func() {
+ if err := c.syncStreamMsg(ctx, conversationID, clientMsgID); err != nil {
+ log.ZError(ctx, "syncStreamMsg failed", err, "conversationID", conversationID, "clientMsgID", clientMsgID)
+ }
+ }()
+}
+
+func (c *Conversation) streamMsgReplace(ctx context.Context, conversationID string, msgs []*sdk_struct.MsgStruct) {
+ for _, msg := range msgs {
+ if msg.ContentType != constant.Stream {
+ continue
+ }
+ var tips sdkws.StreamMsgTips
+ if err := json.Unmarshal([]byte(msg.Content), &tips); err != nil {
+ log.ZError(ctx, "unmarshal stream msg tips failed", err, "msg", msg)
+ continue
+ }
+ if tips.End {
+ continue
+ }
+ c.asyncStreamMsg(ctx, conversationID, msg.ClientMsgID)
+ }
+}
diff --git a/internal/conversation_msg/sync.go b/internal/conversation_msg/sync.go
index f91a75581..88a553155 100644
--- a/internal/conversation_msg/sync.go
+++ b/internal/conversation_msg/sync.go
@@ -21,6 +21,7 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/log"
@@ -30,10 +31,14 @@ func (c *Conversation) SyncAllConversationHashReadSeqs(ctx context.Context) erro
startTime := time.Now()
log.ZDebug(ctx, "start SyncConversationHashReadSeqs")
- seqs, err := c.getServerHasReadAndMaxSeqs(ctx)
+ resp := msg.GetConversationsHasReadAndMaxSeqResp{}
+ req := msg.GetConversationsHasReadAndMaxSeqReq{UserID: c.loginUserID}
+ err := c.SendReqWaitResp(ctx, &req, constant.GetConvMaxReadSeq, &resp)
if err != nil {
+ log.ZWarn(ctx, "SendReqWaitResp err", err)
return err
}
+ seqs := resp.Seqs
log.ZDebug(ctx, "getServerHasReadAndMaxSeqs completed", "duration", time.Since(startTime).Seconds())
if len(seqs) == 0 {
@@ -66,8 +71,8 @@ func (c *Conversation) SyncAllConversationHashReadSeqs(ctx context.Context) erro
unreadCount = int32(v.MaxSeq - v.HasReadSeq)
}
if conversation, ok := conversationsOnLocalMap[conversationID]; ok {
- if conversation.UnreadCount != unreadCount || conversation.HasReadSeq != v.HasReadSeq {
- if err := c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"unread_count": unreadCount, "has_read_seq": v.HasReadSeq}); err != nil {
+ if conversation.UnreadCount != unreadCount {
+ if err := c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"unread_count": unreadCount}); err != nil {
log.ZWarn(ctx, "UpdateColumnsConversation err", err, "conversationID", conversationID)
continue
}
@@ -81,13 +86,13 @@ func (c *Conversation) SyncAllConversationHashReadSeqs(ctx context.Context) erro
if len(conversationIDsNeedSync) > 0 {
stepStartTime = time.Now()
- conversationsOnServer, err := c.getServerConversationsByIDs(ctx, conversationIDsNeedSync)
+ r, err := c.getConversationsByIDsFromServer(ctx, conversationIDsNeedSync)
if err != nil {
log.ZWarn(ctx, "getServerConversationsByIDs err", err, "conversationIDs", conversationIDsNeedSync)
return err
}
log.ZDebug(ctx, "getServerConversationsByIDs completed", "duration", time.Since(stepStartTime).Seconds())
-
+ conversationsOnServer := datautil.Batch(ServerConversationToLocal, r.Conversations)
stepStartTime = time.Now()
if err := c.batchAddFaceURLAndName(ctx, conversationsOnServer...); err != nil {
log.ZWarn(ctx, "batchAddFaceURLAndName err", err, "conversationsOnServer", conversationsOnServer)
@@ -108,7 +113,6 @@ func (c *Conversation) SyncAllConversationHashReadSeqs(ctx context.Context) erro
unreadCount = int32(v.MaxSeq - v.HasReadSeq)
}
conversation.UnreadCount = unreadCount
- conversation.HasReadSeq = v.HasReadSeq
}
stepStartTime = time.Now()
diff --git a/internal/flagconst/flag.go b/internal/flagconst/flag.go
new file mode 100644
index 000000000..c2a595dad
--- /dev/null
+++ b/internal/flagconst/flag.go
@@ -0,0 +1,5 @@
+package flagconst
+
+var (
+ TestMode = false // if it`s true, it is test mode.
+)
diff --git a/internal/friend/hash.go b/internal/friend/hash.go
deleted file mode 100644
index 7e5313802..000000000
--- a/internal/friend/hash.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package friend
-
-import (
- "crypto/md5"
- "encoding/binary"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/protocol/constant"
- "github.com/openimsdk/tools/utils/datautil"
- "strconv"
- "strings"
-)
-
-func (f *Friend) CalculateHash(friends []*model_struct.LocalFriend) uint64 {
- datautil.SortAny(friends, func(a, b *model_struct.LocalFriend) bool {
- return a.CreateTime > b.CreateTime
- })
- if len(friends) > constant.MaxSyncPullNumber {
- friends = friends[:constant.MaxSyncPullNumber]
- }
- hashStr := strings.Join(datautil.Slice(friends, func(f *model_struct.LocalFriend) string {
- return strings.Join([]string{
- f.FriendUserID,
- f.Remark,
- strconv.FormatInt(f.CreateTime, 10),
- strconv.Itoa(int(f.AddSource)),
- f.OperatorUserID,
- f.Ex,
- strconv.FormatBool(f.IsPinned),
- }, ",")
- }), ";")
- sum := md5.Sum([]byte(hashStr))
- return binary.BigEndian.Uint64(sum[:])
-}
diff --git a/internal/friend/sdk.go b/internal/friend/sdk.go
deleted file mode 100644
index 786a9588d..000000000
--- a/internal/friend/sdk.go
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright 2021 OpenIM Corporation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package friend
-
-import (
- "context"
-
- friend "github.com/openimsdk/protocol/relation"
- "github.com/openimsdk/protocol/wrapperspb"
- "github.com/openimsdk/tools/errs"
- "github.com/openimsdk/tools/utils/datautil"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/datafetcher"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
-
- "github.com/openimsdk/tools/log"
-)
-
-func (f *Friend) GetSpecifiedFriendsInfo(ctx context.Context, friendUserIDList []string) ([]*server_api_params.FullUserInfo, error) {
- datafetcher := datafetcher.NewDataFetcher(
- f.db,
- f.friendListTableName(),
- f.loginUserID,
- func(localFriend *model_struct.LocalFriend) string {
- return localFriend.FriendUserID
- },
- func(ctx context.Context, values []*model_struct.LocalFriend) error {
- return f.db.BatchInsertFriend(ctx, values)
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, bool, error) {
- localFriends, err := f.db.GetFriendInfoList(ctx, userIDs)
- return localFriends, true, err
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, error) {
- serverFriend, err := f.GetDesignatedFriends(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerFriendToLocalFriend, serverFriend), nil
- },
- )
- localFriendList, err := datafetcher.FetchMissingAndFillLocal(ctx, friendUserIDList)
- if err != nil {
- return nil, err
- }
-
- log.ZDebug(ctx, "GetDesignatedFriendsInfo", "localFriendList", localFriendList)
- blackList, err := f.db.GetBlackInfoList(ctx, friendUserIDList)
- if err != nil {
- return nil, err
- }
- log.ZDebug(ctx, "GetDesignatedFriendsInfo", "blackList", blackList)
- m := make(map[string]*model_struct.LocalBlack)
- for i, black := range blackList {
- m[black.BlockUserID] = blackList[i]
- }
- res := make([]*server_api_params.FullUserInfo, 0, len(localFriendList))
- for _, localFriend := range localFriendList {
- res = append(res, &server_api_params.FullUserInfo{
- PublicInfo: nil,
- FriendInfo: localFriend,
- BlackInfo: m[localFriend.FriendUserID],
- })
- }
- return res, nil
-}
-
-func (f *Friend) AddFriend(ctx context.Context, userIDReqMsg *friend.ApplyToAddFriendReq) error {
- if userIDReqMsg.FromUserID == "" {
- userIDReqMsg.FromUserID = f.loginUserID
- }
- if err := util.ApiPost(ctx, constant.AddFriendRouter, userIDReqMsg, nil); err != nil {
- return err
- }
-
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- return f.SyncAllFriendApplication(ctx)
-}
-
-func (f *Friend) GetFriendApplicationListAsRecipient(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
- return f.db.GetRecvFriendApplication(ctx)
-}
-
-func (f *Friend) GetFriendApplicationListAsApplicant(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
- return f.db.GetSendFriendApplication(ctx)
-}
-
-func (f *Friend) AcceptFriendApplication(ctx context.Context, userIDHandleMsg *sdk.ProcessFriendApplicationParams) error {
- return f.RespondFriendApply(ctx, &friend.RespondFriendApplyReq{FromUserID: userIDHandleMsg.ToUserID, ToUserID: f.loginUserID, HandleResult: constant.FriendResponseAgree, HandleMsg: userIDHandleMsg.HandleMsg})
-}
-
-func (f *Friend) RefuseFriendApplication(ctx context.Context, userIDHandleMsg *sdk.ProcessFriendApplicationParams) error {
- return f.RespondFriendApply(ctx, &friend.RespondFriendApplyReq{FromUserID: userIDHandleMsg.ToUserID, ToUserID: f.loginUserID, HandleResult: constant.FriendResponseRefuse, HandleMsg: userIDHandleMsg.HandleMsg})
-}
-
-func (f *Friend) RespondFriendApply(ctx context.Context, req *friend.RespondFriendApplyReq) error {
- if req.ToUserID == "" {
- req.ToUserID = f.loginUserID
- }
- if err := util.ApiPost(ctx, constant.AddFriendResponse, req, nil); err != nil {
- return err
- }
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- if req.HandleResult == constant.FriendResponseAgree {
- _ = f.IncrSyncFriends(ctx)
- }
- _ = f.SyncAllFriendApplication(ctx)
- return nil
- // return f.SyncFriendApplication(ctx)
-}
-
-func (f *Friend) CheckFriend(ctx context.Context, friendUserIDList []string) ([]*server_api_params.UserIDResult, error) {
- friendList, err := f.db.GetFriendInfoList(ctx, friendUserIDList)
- if err != nil {
- return nil, err
- }
- blackList, err := f.db.GetBlackInfoList(ctx, friendUserIDList)
- if err != nil {
- return nil, err
- }
- res := make([]*server_api_params.UserIDResult, 0, len(friendUserIDList))
- for _, v := range friendUserIDList {
- var r server_api_params.UserIDResult
- isBlack := false
- isFriend := false
- for _, b := range blackList {
- if v == b.BlockUserID {
- isBlack = true
- break
- }
- }
- for _, f := range friendList {
- if v == f.FriendUserID {
- isFriend = true
- break
- }
- }
- r.UserID = v
- if isFriend && !isBlack {
- r.Result = 1
- } else {
- r.Result = 0
- }
- res = append(res, &r)
- }
- return res, nil
-}
-
-func (f *Friend) DeleteFriend(ctx context.Context, friendUserID string) error {
- if err := util.ApiPost(ctx, constant.DeleteFriendRouter, &friend.DeleteFriendReq{OwnerUserID: f.loginUserID, FriendUserID: friendUserID}, nil); err != nil {
- return err
- }
-
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- return f.IncrSyncFriends(ctx)
-}
-
-// Full GetFriendList
-func (f *Friend) GetFriendList(ctx context.Context) ([]*server_api_params.FullUserInfo, error) {
- localFriendList, err := f.db.GetAllFriendList(ctx)
- if err != nil {
- return nil, err
- }
- localBlackList, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return nil, err
- }
- m := make(map[string]*model_struct.LocalBlack)
- for i, black := range localBlackList {
- m[black.BlockUserID] = localBlackList[i]
- }
- res := make([]*server_api_params.FullUserInfo, 0, len(localFriendList))
- for _, localFriend := range localFriendList {
- res = append(res, &server_api_params.FullUserInfo{
- PublicInfo: nil,
- FriendInfo: localFriend,
- BlackInfo: m[localFriend.FriendUserID],
- })
- }
- return res, nil
-}
-
-func (f *Friend) GetFriendListPage(ctx context.Context, offset, count int32) ([]*server_api_params.FullUserInfo, error) {
- dataFetcher := datafetcher.NewDataFetcher(
- f.db,
- f.friendListTableName(),
- f.loginUserID,
- func(localFriend *model_struct.LocalFriend) string {
- return localFriend.FriendUserID
- },
- func(ctx context.Context, values []*model_struct.LocalFriend) error {
- return f.db.BatchInsertFriend(ctx, values)
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, bool, error) {
- localFriendList, err := f.db.GetFriendInfoList(ctx, userIDs)
- return localFriendList, true, err
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, error) {
- serverFriend, err := f.GetDesignatedFriends(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerFriendToLocalFriend, serverFriend), nil
- },
- )
-
- localFriendList, err := dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
- if err != nil {
- return nil, err
- }
-
- // don't need extra handle. only full pull.
- localBlackList, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return nil, err
- }
- m := make(map[string]*model_struct.LocalBlack)
- for i, black := range localBlackList {
- m[black.BlockUserID] = localBlackList[i]
- }
- res := make([]*server_api_params.FullUserInfo, 0, len(localFriendList))
- for _, localFriend := range localFriendList {
- res = append(res, &server_api_params.FullUserInfo{
- PublicInfo: nil,
- FriendInfo: localFriend,
- BlackInfo: m[localFriend.FriendUserID],
- })
- }
- return res, nil
-}
-
-func (f *Friend) GetFriendListPageV2(ctx context.Context, offset, count int32) (*GetFriendInfoListV2, error) {
- datafetcher := datafetcher.NewDataFetcher(
- f.db,
- f.friendListTableName(),
- f.loginUserID,
- func(localFriend *model_struct.LocalFriend) string {
- return localFriend.FriendUserID
- },
- func(ctx context.Context, values []*model_struct.LocalFriend) error {
- return f.db.BatchInsertFriend(ctx, values)
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, bool, error) {
- localFriendList, err := f.db.GetFriendInfoList(ctx, userIDs)
- return localFriendList, true, err
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, error) {
- serverFriend, err := f.GetDesignatedFriends(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerFriendToLocalFriend, serverFriend), nil
- },
- )
-
- localFriendList, isEnd, err := datafetcher.FetchWithPaginationV2(ctx, int(offset), int(count))
- if err != nil {
- return nil, err
- }
-
- // don't need extra handle. only full pull.
- localBlackList, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return nil, err
- }
- m := make(map[string]*model_struct.LocalBlack)
- for i, black := range localBlackList {
- m[black.BlockUserID] = localBlackList[i]
- }
- fullUserInfo := make([]*server_api_params.FullUserInfo, 0, len(localFriendList))
- for _, localFriend := range localFriendList {
- fullUserInfo = append(fullUserInfo, &server_api_params.FullUserInfo{
- PublicInfo: nil,
- FriendInfo: localFriend,
- BlackInfo: m[localFriend.FriendUserID],
- })
- }
- response := &GetFriendInfoListV2{
- FullUserInfoList: fullUserInfo,
- IsEnd: isEnd,
- }
- return response, nil
-}
-
-func (f *Friend) SearchFriends(ctx context.Context, param *sdk.SearchFriendsParam) ([]*sdk.SearchFriendItem, error) {
- if len(param.KeywordList) == 0 || (!param.IsSearchNickname && !param.IsSearchUserID && !param.IsSearchRemark) {
- return nil, sdkerrs.ErrArgs.WrapMsg("keyword is null or search field all false")
- }
- localFriendList, err := f.db.SearchFriendList(ctx, param.KeywordList[0], param.IsSearchUserID, param.IsSearchNickname, param.IsSearchRemark)
- if err != nil {
- return nil, err
- }
- localBlackList, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return nil, err
- }
- m := make(map[string]struct{})
- for _, black := range localBlackList {
- m[black.BlockUserID] = struct{}{}
- }
- res := make([]*sdk.SearchFriendItem, 0, len(localFriendList))
- for i, localFriend := range localFriendList {
- var relationship int
- if _, ok := m[localFriend.FriendUserID]; ok {
- relationship = constant.BlackRelationship
- } else {
- relationship = constant.FriendRelationship
- }
- res = append(res, &sdk.SearchFriendItem{
- LocalFriend: *localFriendList[i],
- Relationship: relationship,
- })
- }
- return res, nil
-}
-
-func (f *Friend) SetFriendRemark(ctx context.Context, userIDRemark *sdk.SetFriendRemarkParams) error {
- if err := util.ApiPost(ctx, constant.SetFriendRemark, &friend.SetFriendRemarkReq{OwnerUserID: f.loginUserID, FriendUserID: userIDRemark.ToUserID, Remark: userIDRemark.Remark}, nil); err != nil {
- return err
- }
-
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- return f.IncrSyncFriends(ctx)
-}
-
-func (f *Friend) PinFriends(ctx context.Context, friends *sdk.SetFriendPinParams) error {
- if err := util.ApiPost(ctx, constant.UpdateFriends, &friend.UpdateFriendsReq{OwnerUserID: f.loginUserID, FriendUserIDs: friends.ToUserIDs, IsPinned: friends.IsPinned}, nil); err != nil {
- return err
- }
-
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- return f.IncrSyncFriends(ctx)
-}
-
-func (f *Friend) AddBlack(ctx context.Context, blackUserID string, ex string) error {
- if err := util.ApiPost(ctx, constant.AddBlackRouter, &friend.AddBlackReq{OwnerUserID: f.loginUserID, BlackUserID: blackUserID, Ex: ex}, nil); err != nil {
- return err
- }
- return f.SyncAllBlackList(ctx)
-}
-
-func (f *Friend) RemoveBlack(ctx context.Context, blackUserID string) error {
- if err := util.ApiPost(ctx, constant.RemoveBlackRouter, &friend.RemoveBlackReq{OwnerUserID: f.loginUserID, BlackUserID: blackUserID}, nil); err != nil {
- return err
- }
-
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
-
- return f.SyncAllBlackList(ctx)
-}
-
-func (f *Friend) GetBlackList(ctx context.Context) ([]*model_struct.LocalBlack, error) {
- return f.db.GetBlackListDB(ctx)
-}
-
-func (f *Friend) SetFriendsEx(ctx context.Context, friendIDs []string, ex string) error {
- if err := util.ApiPost(ctx, constant.UpdateFriends, &friend.UpdateFriendsReq{OwnerUserID: f.loginUserID, FriendUserIDs: friendIDs, Ex: &wrapperspb.StringValue{
- Value: ex,
- }}, nil); err != nil {
- return err
- }
- // Check if the specified ID is a friend
- friendResults, err := f.CheckFriend(ctx, friendIDs)
- if err != nil {
- return errs.WrapMsg(err, "Error checking friend status")
- }
-
- // Determine if friendID is indeed a friend
- // Iterate over each friendID
- for _, friendID := range friendIDs {
- isFriend := false
-
- // Check if this friendID is in the friendResults
- for _, result := range friendResults {
- if result.UserID == friendID && result.Result == 1 { // Assuming result 1 means they are friends
- isFriend = true
- break
- }
- }
-
- // If this friendID is not a friend, return an error
- if !isFriend {
- return errs.ErrRecordNotFound.WrapMsg("Not friend")
- }
- }
-
- // If the code reaches here, all friendIDs are confirmed as friends
- // Update friend information if they are friends
-
- updateErr := f.db.UpdateColumnsFriend(ctx, friendIDs, map[string]interface{}{"Ex": ex})
- if updateErr != nil {
- return errs.WrapMsg(updateErr, "Error updating friend information")
- }
- return nil
-}
diff --git a/internal/friend/sync.go b/internal/friend/sync.go
deleted file mode 100644
index 3646033bf..000000000
--- a/internal/friend/sync.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package friend
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/openimsdk/tools/utils/datautil"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/protocol/relation"
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-func (f *Friend) SyncBothFriendRequest(ctx context.Context, fromUserID, toUserID string) error {
- var resp relation.GetDesignatedFriendsApplyResp
- if err := util.ApiPost(ctx, constant.GetDesignatedFriendsApplyRouter, &relation.GetDesignatedFriendsApplyReq{FromUserID: fromUserID, ToUserID: toUserID}, &resp); err != nil {
- return nil
- }
- localData, err := f.db.GetBothFriendReq(ctx, fromUserID, toUserID)
- if err != nil {
- return err
- }
- if toUserID == f.loginUserID {
- return f.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, resp.FriendRequests), localData, nil)
- } else if fromUserID == f.loginUserID {
- return f.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, resp.FriendRequests), localData, nil)
- }
- return nil
-}
-
-// send
-func (f *Friend) SyncAllSelfFriendApplication(ctx context.Context) error {
- req := &relation.GetPaginationFriendsApplyFromReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationFriendsApplyFromResp) []*sdkws.FriendRequest {
- return resp.FriendRequests
- }
- requests, err := util.GetPageAll(ctx, constant.GetSelfFriendApplicationListRouter, req, fn)
- if err != nil {
- return err
- }
- localData, err := f.db.GetSendFriendApplication(ctx)
- if err != nil {
- return err
- }
- return f.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil)
-}
-
-func (f *Friend) SyncAllSelfFriendApplicationWithoutNotice(ctx context.Context) error {
- req := &relation.GetPaginationFriendsApplyFromReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationFriendsApplyFromResp) []*sdkws.FriendRequest {
- return resp.FriendRequests
- }
- requests, err := util.GetPageAll(ctx, constant.GetSelfFriendApplicationListRouter, req, fn)
- if err != nil {
- return err
- }
- localData, err := f.db.GetSendFriendApplication(ctx)
- if err != nil {
- return err
- }
- return f.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil, false, true)
-}
-
-// recv
-func (f *Friend) SyncAllFriendApplication(ctx context.Context) error {
- req := &relation.GetPaginationFriendsApplyToReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationFriendsApplyToResp) []*sdkws.FriendRequest {
- return resp.FriendRequests
- }
- requests, err := util.GetPageAll(ctx, constant.GetFriendApplicationListRouter, req, fn)
- if err != nil {
- return err
- }
- localData, err := f.db.GetRecvFriendApplication(ctx)
- if err != nil {
- return err
- }
- return f.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil)
-}
-func (f *Friend) SyncAllFriendApplicationWithoutNotice(ctx context.Context) error {
- req := &relation.GetPaginationFriendsApplyToReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationFriendsApplyToResp) []*sdkws.FriendRequest {
- return resp.FriendRequests
- }
- requests, err := util.GetPageAll(ctx, constant.GetFriendApplicationListRouter, req, fn)
- if err != nil {
- return err
- }
- localData, err := f.db.GetRecvFriendApplication(ctx)
- if err != nil {
- return err
- }
- return f.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil, false, true)
-}
-
-func (f *Friend) SyncAllFriendList(ctx context.Context) error {
- t := time.Now()
- defer func(start time.Time) {
-
- elapsed := time.Since(start).Milliseconds()
- log.ZDebug(ctx, "SyncAllFriendList fn call end", "cost time", fmt.Sprintf("%d ms", elapsed))
-
- }(t)
- return f.IncrSyncFriends(ctx)
-}
-
-func (f *Friend) SyncAllBlackList(ctx context.Context) error {
- req := &relation.GetPaginationBlacksReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationBlacksResp) []*sdkws.BlackInfo { return resp.Blacks }
- serverData, err := util.GetPageAll(ctx, constant.GetBlackListRouter, req, fn)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "black from server", "data", serverData)
- localData, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "black from local", "data", localData)
- return f.blockSyncer.Sync(ctx, datautil.Batch(ServerBlackToLocalBlack, serverData), localData, nil)
-}
-
-func (f *Friend) SyncAllBlackListWithoutNotice(ctx context.Context) error {
- req := &relation.GetPaginationBlacksReq{UserID: f.loginUserID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *relation.GetPaginationBlacksResp) []*sdkws.BlackInfo { return resp.Blacks }
- serverData, err := util.GetPageAll(ctx, constant.GetBlackListRouter, req, fn)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "black from server", "data", serverData)
- localData, err := f.db.GetBlackListDB(ctx)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "black from local", "data", localData)
- return f.blockSyncer.Sync(ctx, datautil.Batch(ServerBlackToLocalBlack, serverData), localData, nil, false, true)
-}
-
-func (f *Friend) GetDesignatedFriends(ctx context.Context, friendIDs []string) ([]*sdkws.FriendInfo, error) {
- resp := &relation.GetDesignatedFriendsResp{}
- if err := util.ApiPost(ctx, constant.GetDesignatedFriendsRouter, &relation.GetDesignatedFriendsReq{OwnerUserID: f.loginUserID, FriendUserIDs: friendIDs}, &resp); err != nil {
- return nil, err
- }
- return resp.FriendsInfo, nil
-}
diff --git a/internal/friend/sync2.go b/internal/friend/sync2.go
deleted file mode 100644
index 445f32006..000000000
--- a/internal/friend/sync2.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package friend
-
-import (
- "context"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/incrversion"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- friend "github.com/openimsdk/protocol/relation"
- "github.com/openimsdk/tools/utils/datautil"
-)
-
-func (f *Friend) IncrSyncFriends(ctx context.Context) error {
- friendSyncer := incrversion.VersionSynchronizer[*model_struct.LocalFriend, *friend.GetIncrementalFriendsResp]{
- Ctx: ctx,
- DB: f.db,
- TableName: f.friendListTableName(),
- EntityID: f.loginUserID,
- Key: func(localFriend *model_struct.LocalFriend) string {
- return localFriend.FriendUserID
- },
- Local: func() ([]*model_struct.LocalFriend, error) {
- return f.db.GetAllFriendList(ctx)
- },
- Server: func(version *model_struct.LocalVersionSync) (*friend.GetIncrementalFriendsResp, error) {
- return util.CallApi[friend.GetIncrementalFriendsResp](ctx, constant.GetIncrementalFriends, &friend.GetIncrementalFriendsReq{
- UserID: f.loginUserID,
- Version: version.Version,
- VersionID: version.VersionID,
- })
- },
- Full: func(resp *friend.GetIncrementalFriendsResp) bool {
- return resp.Full
- },
- Version: func(resp *friend.GetIncrementalFriendsResp) (string, uint64) {
- return resp.VersionID, resp.Version
- },
- Delete: func(resp *friend.GetIncrementalFriendsResp) []string {
- return resp.Delete
- },
- Update: func(resp *friend.GetIncrementalFriendsResp) []*model_struct.LocalFriend {
- return datautil.Batch(ServerFriendToLocalFriend, resp.Update)
- },
- Insert: func(resp *friend.GetIncrementalFriendsResp) []*model_struct.LocalFriend {
- return datautil.Batch(ServerFriendToLocalFriend, resp.Insert)
- },
- Syncer: func(server, local []*model_struct.LocalFriend) error {
- return f.friendSyncer.Sync(ctx, server, local, nil)
- },
- FullSyncer: func(ctx context.Context) error {
- return f.friendSyncer.FullSync(ctx, f.loginUserID)
- },
- FullID: func(ctx context.Context) ([]string, error) {
- resp, err := util.CallApi[friend.GetFullFriendUserIDsResp](ctx, constant.GetFullFriendUserIDs, &friend.GetFullFriendUserIDsReq{
- UserID: f.loginUserID,
- })
- if err != nil {
- return nil, err
- }
- return resp.UserIDs, nil
- },
- }
- return friendSyncer.Sync()
-}
-
-func (f *Friend) friendListTableName() string {
- return model_struct.LocalFriend{}.TableName()
-}
diff --git a/internal/friend/sync2_test.go b/internal/friend/sync2_test.go
deleted file mode 100644
index d782b69b0..000000000
--- a/internal/friend/sync2_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package friend
-
-import (
- "fmt"
- "testing"
-)
-
-func Test_main(t *testing.T) {
- a := []int{1, 2, 3, 4, 5}
- fmt.Println(a[:3])
- fmt.Println(a[3:])
- fmt.Println(a[2:4])
-}
diff --git a/internal/friend/types.go b/internal/friend/types.go
deleted file mode 100644
index 5429b5657..000000000
--- a/internal/friend/types.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package friend
-
-import "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
-
-type GetFriendInfoListV2 struct {
- FullUserInfoList []*server_api_params.FullUserInfo
- IsEnd bool `json:"isEnd"`
-}
diff --git a/internal/full/full.go b/internal/full/full.go
deleted file mode 100644
index 050cf6ac0..000000000
--- a/internal/full/full.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package full
-
-import (
- "context"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/internal/friend"
- "github.com/openimsdk/openim-sdk-core/v3/internal/group"
- "github.com/openimsdk/openim-sdk-core/v3/internal/user"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-)
-
-type Full struct {
- user *user.User
- friend *friend.Friend
- group *group.Group
- ch chan common.Cmd2Value
- db db_interface.DataBase
-}
-
-func (u *Full) Group() *group.Group {
- return u.group
-}
-
-func NewFull(user *user.User, friend *friend.Friend, group *group.Group, ch chan common.Cmd2Value,
- db db_interface.DataBase) *Full {
- return &Full{user: user, friend: friend, group: group, ch: ch, db: db}
-}
-
-func (u *Full) GetGroupInfoFromLocal2Svr(ctx context.Context, groupID string, sessionType int32) (*model_struct.LocalGroup, error) {
- switch sessionType {
- case constant.GroupChatType:
- return u.group.GetGroupInfoFromLocal2Svr(ctx, groupID)
- case constant.SuperGroupChatType:
- return u.GetGroupInfoByGroupID(ctx, groupID)
- default:
- return nil, fmt.Errorf("sessionType is not support %d", sessionType)
- }
-}
-func (u *Full) GetReadDiffusionGroupIDList(ctx context.Context) ([]string, error) {
- g, err := u.group.GetJoinedDiffusionGroupIDListFromSvr(ctx)
- if err != nil {
- return nil, err
- }
- return g, err
-}
diff --git a/internal/full/full_model.go b/internal/full/full_model.go
deleted file mode 100644
index 73e65cc46..000000000
--- a/internal/full/full_model.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package full
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-)
-
-func (u *Full) GetGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
- g2, err := u.group.GetGroupInfoFromLocal2Svr(ctx, groupID)
- return g2, err
-}
-
-func (u *Full) GetGroupsInfo(ctx context.Context, groupIDs ...string) (map[string]*model_struct.LocalGroup, error) {
- return u.group.GetGroupsInfoFromLocal2Svr(ctx, groupIDs...)
-}
diff --git a/internal/full/open_im_sdk_full.go b/internal/full/open_im_sdk_full.go
deleted file mode 100644
index ca26b5d6a..000000000
--- a/internal/full/open_im_sdk_full.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package full
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- api "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-func (u *Full) GetUsersInfo(ctx context.Context, userIDs []string) ([]*api.FullUserInfo, error) {
- friendList, err := u.db.GetFriendInfoList(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- blackList, err := u.db.GetBlackInfoList(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- users, err := u.user.GetServerUserInfo(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- friendMap := make(map[string]*model_struct.LocalFriend)
- for i, f := range friendList {
- friendMap[f.FriendUserID] = friendList[i]
- }
- blackMap := make(map[string]*model_struct.LocalBlack)
- for i, b := range blackList {
- blackMap[b.BlockUserID] = blackList[i]
- }
- userMap := make(map[string]*api.PublicUser)
- for _, info := range users {
- userMap[info.UserID] = &api.PublicUser{
- UserID: info.UserID,
- Nickname: info.Nickname,
- FaceURL: info.FaceURL,
- Ex: info.Ex,
- CreateTime: info.CreateTime,
- }
- }
- res := make([]*api.FullUserInfo, 0, len(users))
- for _, userID := range userIDs {
- info, ok := userMap[userID]
- if !ok {
- continue
- }
- res = append(res, &api.FullUserInfo{
- PublicInfo: info,
- FriendInfo: friendMap[userID],
- BlackInfo: blackMap[userID],
- })
-
- // update single conversation
-
- conversation, err := u.db.GetConversationByUserID(ctx, userID)
- if err != nil {
- log.ZWarn(ctx, "GetConversationByUserID failed", err, "userID", userID)
- } else {
- if _, ok := friendMap[userID]; ok {
- continue
- }
- log.ZDebug(ctx, "GetConversationByUserID", "conversation", conversation)
- if conversation.ShowName != info.Nickname || conversation.FaceURL != info.FaceURL {
- _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.UpdateConFaceUrlAndNickName,
- Args: common.SourceIDAndSessionType{SourceID: userID, SessionType: conversation.ConversationType, FaceURL: info.FaceURL, Nickname: info.Nickname}}, u.ch)
- _ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{Action: constant.UpdateMsgFaceUrlAndNickName,
- Args: common.UpdateMessageInfo{SessionType: conversation.ConversationType, UserID: userID, FaceURL: info.FaceURL, Nickname: info.Nickname}}, u.ch)
- }
- }
- }
- return res, nil
-}
-
-func (u *Full) GetUsersInfoWithCache(ctx context.Context, userIDs []string, groupID string) ([]*api.FullUserInfoWithCache, error) {
- friendList, err := u.db.GetFriendInfoList(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- blackList, err := u.db.GetBlackInfoList(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- users, err := u.user.GetServerUserInfo(ctx, userIDs)
- if err == nil {
- var strangers []*model_struct.LocalStranger
- for _, val := range users {
- strangerTemp := &model_struct.LocalStranger{
- UserID: val.UserID,
- Nickname: val.Nickname,
- FaceURL: val.FaceURL,
- CreateTime: val.CreateTime,
- AppMangerLevel: val.AppMangerLevel,
- Ex: val.Ex,
- AttachedInfo: val.Ex,
- GlobalRecvMsgOpt: val.GlobalRecvMsgOpt,
- }
- strangers = append(strangers, strangerTemp)
- }
- err := u.db.SetStrangerInfo(ctx, strangers)
- if err != nil {
- return nil, err
- }
- } else {
- strangerList, err := u.db.GetStrangerInfo(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- for _, val := range strangerList {
- userTemp := &sdkws.UserInfo{
- UserID: val.UserID,
- Nickname: val.Nickname,
- FaceURL: val.FaceURL,
- Ex: val.Ex,
- CreateTime: val.CreateTime,
- AppMangerLevel: val.AppMangerLevel,
- GlobalRecvMsgOpt: val.GlobalRecvMsgOpt,
- }
- users = append(users, userTemp)
- }
- }
- var groupMemberList []*model_struct.LocalGroupMember
- if groupID != "" {
- groupMemberList, err = u.db.GetGroupSomeMemberInfo(ctx, groupID, userIDs)
- if err != nil {
- return nil, err
- }
- }
- friendMap := make(map[string]*model_struct.LocalFriend)
- for i, f := range friendList {
- friendMap[f.FriendUserID] = friendList[i]
- }
- blackMap := make(map[string]*model_struct.LocalBlack)
- for i, b := range blackList {
- blackMap[b.BlockUserID] = blackList[i]
- }
- groupMemberMap := make(map[string]*model_struct.LocalGroupMember)
- for i, b := range groupMemberList {
- groupMemberMap[b.UserID] = groupMemberList[i]
- }
- userMap := make(map[string]*api.PublicUser)
- for _, info := range users {
- userMap[info.UserID] = &api.PublicUser{
- UserID: info.UserID,
- Nickname: info.Nickname,
- FaceURL: info.FaceURL,
- Ex: info.Ex,
- CreateTime: info.CreateTime,
- }
- }
- res := make([]*api.FullUserInfoWithCache, 0, len(users))
- for _, userID := range userIDs {
- info, ok := userMap[userID]
- if !ok {
- continue
- }
- res = append(res, &api.FullUserInfoWithCache{
- PublicInfo: info,
- FriendInfo: friendMap[userID],
- BlackInfo: blackMap[userID],
- GroupMemberInfo: groupMemberMap[userID],
- })
-
- // update single conversation
-
- conversation, err := u.db.GetConversationByUserID(ctx, userID)
- if err != nil {
- log.ZWarn(ctx, "GetConversationByUserID failed", err, "userID", userID)
- } else {
- if _, ok := friendMap[userID]; ok {
- continue
- }
- log.ZDebug(ctx, "GetConversationByUserID", "conversation", conversation)
- if conversation.ShowName != info.Nickname || conversation.FaceURL != info.FaceURL {
- _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.UpdateConFaceUrlAndNickName,
- Args: common.SourceIDAndSessionType{SourceID: userID, SessionType: conversation.ConversationType, FaceURL: info.FaceURL, Nickname: info.Nickname}}, u.ch)
- _ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{Action: constant.UpdateMsgFaceUrlAndNickName,
- Args: common.UpdateMessageInfo{SessionType: conversation.ConversationType, UserID: userID, FaceURL: info.FaceURL, Nickname: info.Nickname}}, u.ch)
- }
- }
- }
- return res, nil
-}
diff --git a/internal/group/sdk.go b/internal/group/api.go
similarity index 64%
rename from internal/group/sdk.go
rename to internal/group/api.go
index 419410195..28c186e1e 100644
--- a/internal/group/sdk.go
+++ b/internal/group/api.go
@@ -24,7 +24,6 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/datafetcher"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
@@ -32,7 +31,6 @@ import (
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/protocol/wrapperspb"
)
func (g *Group) CreateGroup(ctx context.Context, req *group.CreateGroupReq) (*sdkws.GroupInfo, error) {
@@ -43,7 +41,7 @@ func (g *Group) CreateGroup(ctx context.Context, req *group.CreateGroupReq) (*sd
return nil, sdkerrs.ErrGroupType
}
req.GroupInfo.CreatorUserID = g.loginUserID
- resp, err := util.CallApi[group.CreateGroupResp](ctx, constant.CreateGroupRouter, req)
+ resp, err := g.createGroup(ctx, req)
if err != nil {
return nil, err
}
@@ -61,7 +59,8 @@ func (g *Group) CreateGroup(ctx context.Context, req *group.CreateGroupReq) (*sd
}
func (g *Group) JoinGroup(ctx context.Context, groupID, reqMsg string, joinSource int32, ex string) error {
- if err := util.ApiPost(ctx, constant.JoinGroupRouter, &group.JoinGroupReq{GroupID: groupID, ReqMessage: reqMsg, JoinSource: joinSource, InviterUserID: g.loginUserID, Ex: ex}, nil); err != nil {
+ req := &group.JoinGroupReq{GroupID: groupID, ReqMessage: reqMsg, JoinSource: joinSource, InviterUserID: g.loginUserID, Ex: ex}
+ if err := g.joinGroup(ctx, req); err != nil {
return err
}
if err := g.SyncSelfGroupApplications(ctx, groupID); err != nil {
@@ -71,10 +70,9 @@ func (g *Group) JoinGroup(ctx context.Context, groupID, reqMsg string, joinSourc
}
func (g *Group) QuitGroup(ctx context.Context, groupID string) error {
- if err := util.ApiPost(ctx, constant.QuitGroupRouter, &group.QuitGroupReq{GroupID: groupID}, nil); err != nil {
+ if err := g.quitGroup(ctx, groupID); err != nil {
return err
}
-
g.groupSyncMutex.Lock()
defer g.groupSyncMutex.Unlock()
@@ -85,10 +83,9 @@ func (g *Group) QuitGroup(ctx context.Context, groupID string) error {
}
func (g *Group) DismissGroup(ctx context.Context, groupID string) error {
- if err := util.ApiPost(ctx, constant.DismissGroupRouter, &group.DismissGroupReq{GroupID: groupID}, nil); err != nil {
+ if err := g.dismissGroup(ctx, groupID); err != nil {
return err
}
-
g.groupSyncMutex.Lock()
defer g.groupSyncMutex.Unlock()
@@ -98,23 +95,23 @@ func (g *Group) DismissGroup(ctx context.Context, groupID string) error {
return nil
}
-func (g *Group) SetGroupApplyMemberFriend(ctx context.Context, groupID string, rule int32) error {
- return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, ApplyMemberFriend: wrapperspb.Int32(rule)})
-}
-
-func (g *Group) SetGroupLookMemberInfo(ctx context.Context, groupID string, rule int32) error {
- return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, LookMemberInfo: wrapperspb.Int32(rule)})
-}
-
-func (g *Group) SetGroupVerification(ctx context.Context, groupID string, verification int32) error {
- return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, NeedVerification: wrapperspb.Int32(verification)})
-}
+//func (g *Group) SetGroupApplyMemberFriend(ctx context.Context, groupID string, rule int32) error {
+// return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, ApplyMemberFriend: wrapperspb.Int32(rule)})
+//}
+//
+//func (g *Group) SetGroupLookMemberInfo(ctx context.Context, groupID string, rule int32) error {
+// return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, LookMemberInfo: wrapperspb.Int32(rule)})
+//}
+//
+//func (g *Group) SetGroupVerification(ctx context.Context, groupID string, verification int32) error {
+// return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, NeedVerification: wrapperspb.Int32(verification)})
+//}
func (g *Group) ChangeGroupMute(ctx context.Context, groupID string, isMute bool) (err error) {
if isMute {
- err = util.ApiPost(ctx, constant.MuteGroupRouter, &group.MuteGroupReq{GroupID: groupID}, nil)
+ err = g.muteGroup(ctx, groupID)
} else {
- err = util.ApiPost(ctx, constant.CancelMuteGroupRouter, &group.CancelMuteGroupReq{GroupID: groupID}, nil)
+ err = g.cancelMuteGroup(ctx, groupID)
}
if err != nil {
return err
@@ -129,23 +126,19 @@ func (g *Group) ChangeGroupMute(ctx context.Context, groupID string, isMute bool
return nil
}
-func (g *Group) ChangeGroupMemberMute(ctx context.Context, groupID, userID string, mutedSeconds int) (err error) {
+func (g *Group) ChangeGroupMemberMute(ctx context.Context, groupID, userID string, mutedSeconds int) error {
if mutedSeconds == 0 {
- err = util.ApiPost(ctx, constant.CancelMuteGroupMemberRouter, &group.CancelMuteGroupMemberReq{GroupID: groupID, UserID: userID}, nil)
+ return g.cancelMuteGroupMember(ctx, &group.CancelMuteGroupMemberReq{GroupID: groupID, UserID: userID})
} else {
- err = util.ApiPost(ctx, constant.MuteGroupMemberRouter, &group.MuteGroupMemberReq{GroupID: groupID, UserID: userID, MutedSeconds: uint32(mutedSeconds)}, nil)
+ return g.muteGroupMember(ctx, &group.MuteGroupMemberReq{GroupID: groupID, UserID: userID, MutedSeconds: uint32(mutedSeconds)})
}
- if err != nil {
- return err
- }
- return nil
}
func (g *Group) TransferGroupOwner(ctx context.Context, groupID, newOwnerUserID string) error {
- if err := util.ApiPost(ctx, constant.TransferGroupRouter, &group.TransferGroupOwnerReq{GroupID: groupID, OldOwnerUserID: g.loginUserID, NewOwnerUserID: newOwnerUserID}, nil); err != nil {
+ req := &group.TransferGroupOwnerReq{GroupID: groupID, OldOwnerUserID: g.loginUserID, NewOwnerUserID: newOwnerUserID}
+ if err := g.transferGroup(ctx, req); err != nil {
return err
}
-
g.groupSyncMutex.Lock()
defer g.groupSyncMutex.Unlock()
@@ -156,7 +149,8 @@ func (g *Group) TransferGroupOwner(ctx context.Context, groupID, newOwnerUserID
}
func (g *Group) KickGroupMember(ctx context.Context, groupID string, reason string, userIDList []string) error {
- if err := util.ApiPost(ctx, constant.KickGroupMemberRouter, &group.KickGroupMemberReq{GroupID: groupID, KickedUserIDs: userIDList, Reason: reason}, nil); err != nil {
+ req := &group.KickGroupMemberReq{GroupID: groupID, KickedUserIDs: userIDList, Reason: reason}
+ if err := g.kickGroupMember(ctx, req); err != nil {
return err
}
@@ -166,8 +160,8 @@ func (g *Group) KickGroupMember(ctx context.Context, groupID string, reason stri
return g.IncrSyncGroupAndMember(ctx, groupID)
}
-func (g *Group) SetGroupInfo(ctx context.Context, groupInfo *sdkws.GroupInfoForSet) error {
- if err := util.ApiPost(ctx, constant.SetGroupInfoRouter, &group.SetGroupInfoReq{GroupInfoForSet: groupInfo}, nil); err != nil {
+func (g *Group) SetGroupInfo(ctx context.Context, groupInfo *group.SetGroupInfoExReq) error {
+ if err := g.setGroupInfo(ctx, groupInfo); err != nil {
return err
}
@@ -178,7 +172,8 @@ func (g *Group) SetGroupInfo(ctx context.Context, groupInfo *sdkws.GroupInfoForS
}
func (g *Group) SetGroupMemberInfo(ctx context.Context, groupMemberInfo *group.SetGroupMemberInfo) error {
- if err := util.ApiPost(ctx, constant.SetGroupMemberInfoRouter, &group.SetGroupMemberInfoReq{Members: []*group.SetGroupMemberInfo{groupMemberInfo}}, nil); err != nil {
+ req := &group.SetGroupMemberInfoReq{Members: []*group.SetGroupMemberInfo{groupMemberInfo}}
+ if err := g.setGroupMemberInfo(ctx, req); err != nil {
return err
}
@@ -188,13 +183,13 @@ func (g *Group) SetGroupMemberInfo(ctx context.Context, groupMemberInfo *group.S
return g.IncrSyncGroupAndMember(ctx, groupMemberInfo.GroupID)
}
-func (g *Group) SetGroupMemberRoleLevel(ctx context.Context, groupID, userID string, roleLevel int) error {
- return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, RoleLevel: wrapperspb.Int32(int32(roleLevel))})
-}
-
-func (g *Group) SetGroupMemberNickname(ctx context.Context, groupID, userID string, groupMemberNickname string) error {
- return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, Nickname: wrapperspb.String(groupMemberNickname)})
-}
+//func (g *Group) SetGroupMemberRoleLevel(ctx context.Context, groupID, userID string, roleLevel int) error {
+// return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, RoleLevel: wrapperspb.Int32(int32(roleLevel))})
+//}
+//
+//func (g *Group) SetGroupMemberNickname(ctx context.Context, groupID, userID string, groupMemberNickname string) error {
+// return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, Nickname: wrapperspb.String(groupMemberNickname)})
+//}
func (g *Group) GetJoinedGroupList(ctx context.Context) ([]*model_struct.LocalGroup, error) {
return g.db.GetJoinedGroupListDB(ctx)
@@ -216,7 +211,7 @@ func (g *Group) GetJoinedGroupListPage(ctx context.Context, offset, count int32)
return localGroups, true, err
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
- serverGroupInfo, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
+ serverGroupInfo, err := g.getGroupsInfoFromServer(ctx, groupIDs)
if err != nil {
return nil, err
}
@@ -242,7 +237,7 @@ func (g *Group) GetSpecifiedGroupsInfo(ctx context.Context, groupIDs []string) (
return localGroups, true, err
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
- serverGroupInfo, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
+ serverGroupInfo, err := g.getGroupsInfoFromServer(ctx, groupIDs)
if err != nil {
return nil, err
}
@@ -252,42 +247,6 @@ func (g *Group) GetSpecifiedGroupsInfo(ctx context.Context, groupIDs []string) (
return dataFetcher.FetchMissingAndCombineLocal(ctx, groupIDs)
}
-func (g *Group) GetJoinedGroupListPageV2(ctx context.Context, offset, count int32) (*GetGroupListV2Response, error) {
- dataFetcher := datafetcher.NewDataFetcher(
- g.db,
- g.groupTableName(),
- g.loginUserID,
- func(localGroup *model_struct.LocalGroup) string {
- return localGroup.GroupID
- },
- func(ctx context.Context, values []*model_struct.LocalGroup) error {
- return g.db.BatchInsertGroup(ctx, values)
- },
- func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, bool, error) {
- localGroups, err := g.db.GetGroups(ctx, groupIDs)
- return localGroups, true, err
- },
- func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
- serverGroupInfo, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerGroupToLocalGroup, serverGroupInfo), nil
- },
- )
-
- groupsList, isEnd, err := dataFetcher.FetchWithPaginationV2(ctx, int(offset), int(count))
- if err != nil {
- return nil, err
- }
-
- resp := &GetGroupListV2Response{
- GroupsList: groupsList,
- IsEnd: isEnd,
- }
- return resp, nil
-}
-
func (g *Group) SearchGroups(ctx context.Context, param sdk_params_callback.SearchGroupsParam) ([]*model_struct.LocalGroup, error) {
if len(param.KeywordList) == 0 || (!param.IsSearchGroupName && !param.IsSearchGroupID) {
return nil, sdkerrs.ErrArgs.WrapMsg("keyword is null or search field all false")
@@ -299,18 +258,6 @@ func (g *Group) SearchGroups(ctx context.Context, param sdk_params_callback.Sear
return groups, nil
}
-// funcation (g *Group) SetGroupInfo(ctx context.Context, groupInfo *sdk_params_callback.SetGroupInfoParam, groupID string) error {
-// return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{
-// GroupID: groupID,
-// GroupName: groupInfo.GroupName,
-// Notification: groupInfo.Notification,
-// Introduction: groupInfo.Introduction,
-// FaceURL: groupInfo.FaceURL,
-// Ex: groupInfo.Ex,
-// NeedVerification: wrapperspb.Int32Ptr(groupInfo.NeedVerification),
-// })
-// }
-
func (g *Group) GetGroupMemberOwnerAndAdmin(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupMemberOwnerAndAdminDB(ctx, groupID)
}
@@ -346,45 +293,6 @@ func (g *Group) GetGroupMemberListByJoinTimeFilter(ctx context.Context, groupID
return dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
}
-func (g *Group) GetGroupMemberListByJoinTimeFilterV2(ctx context.Context, groupID string, offset, count int32, joinTimeBegin, joinTimeEnd int64, userIDs []string) (*GetGroupMemberListV2Response, error) {
- if joinTimeEnd == 0 {
- joinTimeEnd = time.Now().UnixMilli()
- }
-
- dataFetcher := datafetcher.NewDataFetcher(
- g.db,
- g.groupAndMemberVersionTableName(),
- groupID,
- func(localGroupMember *model_struct.LocalGroupMember) string {
- return localGroupMember.UserID
- },
- func(ctx context.Context, values []*model_struct.LocalGroupMember) error {
- return g.db.BatchInsertGroupMember(ctx, values)
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, bool, error) {
- localGroupMembers, err := g.db.GetGroupMemberListSplitByJoinTimeFilter(ctx, groupID, int(offset), int(count), joinTimeBegin, joinTimeEnd, userIDs)
- return localGroupMembers, true, err
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
- serverGroupMember, err := g.GetDesignatedGroupMembers(ctx, groupID, userIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerGroupMemberToLocalGroupMember, serverGroupMember), nil
- },
- )
-
- groupMembersList, isEnd, err := dataFetcher.FetchWithPaginationV2(ctx, int(offset), int(count))
- if err != nil {
- return nil, err
- }
- resp := &GetGroupMemberListV2Response{
- GroupMembersList: groupMembersList,
- IsEnd: isEnd,
- }
- return resp, nil
-}
-
func (g *Group) GetSpecifiedGroupMembersInfo(ctx context.Context, groupID string, userIDList []string) ([]*model_struct.LocalGroupMember, error) {
g.groupSyncMutex.Lock()
defer g.groupSyncMutex.Unlock()
@@ -397,7 +305,7 @@ func (g *Group) GetSpecifiedGroupMembersInfo(ctx context.Context, groupID string
_, err := g.db.GetVersionSync(ctx, g.groupAndMemberVersionTableName(), groupID)
if err != nil {
- if errs.Unwrap(err) != errs.ErrRecordNotFound {
+ if !errs.ErrRecordNotFound.Is(err) {
return nil, err
}
err := g.IncrSyncGroupAndMember(ctx, groupID)
@@ -458,7 +366,7 @@ func (g *Group) GetGroupMemberList(ctx context.Context, groupID string, filter,
_, err := g.db.GetVersionSync(ctx, g.groupAndMemberVersionTableName(), groupID)
if err != nil {
- if errs.Unwrap(err) != errs.ErrRecordNotFound {
+ if !errs.ErrRecordNotFound.Is(err) {
return nil, err
}
err := g.IncrSyncGroupAndMember(ctx, groupID)
@@ -493,7 +401,9 @@ func (g *Group) GetGroupMemberList(ctx context.Context, groupID string, filter,
case constant.GroupFilterOwnerAndAdmin:
return localGroupMembers, false, nil
case constant.GroupFilterAll:
+ fallthrough
case constant.GroupFilterOrdinaryUsers:
+ fallthrough
case constant.GroupFilterAdminAndOrdinaryUsers:
return localGroupMembers, true, nil
}
@@ -524,41 +434,6 @@ func (g *Group) GetGroupMemberList(ctx context.Context, groupID string, filter,
return dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
}
-func (g *Group) GetGroupMemberListV2(ctx context.Context, groupID string, filter, offset, count int32) (*GetGroupMemberListV2Response, error) {
- dataFetcher := datafetcher.NewDataFetcher(
- g.db,
- g.groupAndMemberVersionTableName(),
- groupID,
- func(localGroupMember *model_struct.LocalGroupMember) string {
- return localGroupMember.UserID
- },
- func(ctx context.Context, values []*model_struct.LocalGroupMember) error {
- return g.db.BatchInsertGroupMember(ctx, values)
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, bool, error) {
- localGroupMembers, err := g.db.GetGroupMemberListByUserIDs(ctx, groupID, filter, userIDs)
- return localGroupMembers, true, err
- },
- func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
- serverGroupMember, err := g.GetDesignatedGroupMembers(ctx, groupID, userIDs)
- if err != nil {
- return nil, err
- }
- return datautil.Batch(ServerGroupMemberToLocalGroupMember, serverGroupMember), nil
- },
- )
- groupMembersList, isEnd, err := dataFetcher.FetchWithPaginationV2(ctx, int(offset), int(count))
- if err != nil {
- return nil, err
- }
- resp := &GetGroupMemberListV2Response{
- GroupMembersList: groupMembersList,
- IsEnd: isEnd,
- }
-
- return resp, nil
-}
-
func (g *Group) GetGroupApplicationListAsRecipient(ctx context.Context) ([]*model_struct.LocalAdminGroupRequest, error) {
return g.db.GetAdminGroupApplication(ctx)
}
@@ -613,11 +488,11 @@ func (g *Group) GetUsersInGroup(ctx context.Context, groupID string, userIDList
}
return usersInGroup, nil
-
}
func (g *Group) InviteUserToGroup(ctx context.Context, groupID, reason string, userIDList []string) error {
- if err := util.ApiPost(ctx, constant.InviteUserToGroupRouter, &group.InviteUserToGroupReq{GroupID: groupID, Reason: reason, InvitedUserIDs: userIDList}, nil); err != nil {
+ req := &group.InviteUserToGroupReq{GroupID: groupID, Reason: reason, InvitedUserIDs: userIDList}
+ if err := g.inviteUserToGroup(ctx, req); err != nil {
return err
}
@@ -639,13 +514,17 @@ func (g *Group) RefuseGroupApplication(ctx context.Context, groupID, fromUserID,
}
func (g *Group) HandlerGroupApplication(ctx context.Context, req *group.GroupApplicationResponseReq) error {
- if err := util.ApiPost(ctx, constant.AcceptGroupApplicationRouter, req, nil); err != nil {
+ if err := g.handlerGroupApplication(ctx, req); err != nil {
return err
}
// SyncAdminGroupApplication todo
return nil
}
+func (g *Group) GetGroupMemberNameAndFaceURL(ctx context.Context, groupID string, userIDs []string) (map[string]*model_struct.LocalGroupMember, error) {
+ return g.GetGroupMembersInfo(ctx, groupID, userIDs)
+}
+
//func (g *Group) SearchGroupMembersV2(ctx context.Context, req *group.SearchGroupMemberReq) ([]*model_struct.LocalGroupMember, error) {
// if err := req.Check(); err != nil {
// return nil, err
@@ -664,20 +543,3 @@ func (g *Group) HandlerGroupApplication(ctx context.Context, req *group.GroupApp
// }
// return datautil.Slice(resp.Members, g.pbGroupMemberToLocal), nil
//}
-
-func (g *Group) pbGroupMemberToLocal(pb *sdkws.GroupMemberFullInfo) *model_struct.LocalGroupMember {
- return &model_struct.LocalGroupMember{
- GroupID: pb.GroupID,
- UserID: pb.UserID,
- Nickname: pb.Nickname,
- FaceURL: pb.FaceURL,
- RoleLevel: pb.RoleLevel,
- JoinTime: pb.JoinTime,
- JoinSource: pb.JoinSource,
- InviterUserID: pb.InviterUserID,
- MuteEndTime: pb.MuteEndTime,
- OperatorUserID: pb.OperatorUserID,
- Ex: pb.Ex,
- // AttachedInfo: pb.AttachedInfo,
- }
-}
diff --git a/internal/group/cache.go b/internal/group/cache.go
new file mode 100644
index 000000000..8e9e5c397
--- /dev/null
+++ b/internal/group/cache.go
@@ -0,0 +1,68 @@
+package group
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+func (g *Group) buildGroupMemberKey(groupID string, userID string) string {
+ return groupID + ":" + userID
+}
+
+func (g *Group) GetGroupMembersInfoFunc(ctx context.Context, groupID string, userIDs []string,
+ fetchFunc func(ctx context.Context, missingKeys []string) ([]*model_struct.LocalGroupMember, error),
+) (map[string]*model_struct.LocalGroupMember, error) {
+ var (
+ res = make(map[string]*model_struct.LocalGroupMember)
+ missingKeys []string
+ )
+
+ for _, userID := range userIDs {
+ key := g.buildGroupMemberKey(groupID, userID)
+ if member, ok := g.groupMemberCache.Load(key); ok {
+ res[userID] = member
+ } else {
+ missingKeys = append(missingKeys, userIDs...)
+ }
+ }
+
+ log.ZDebug(ctx, "GetGroupMembersInfoFunc fetch", "missingKeys", missingKeys)
+ fetchData, err := fetchFunc(ctx, missingKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ for i, data := range fetchData {
+ key := g.buildGroupMemberKey(groupID, data.UserID)
+ res[data.UserID] = fetchData[i]
+ g.groupMemberCache.Store(key, fetchData[i])
+ }
+
+ return res, nil
+}
+
+func (g *Group) GetGroupMembersInfo(ctx context.Context, groupID string, userIDs []string) (map[string]*model_struct.LocalGroupMember, error) {
+ return g.GetGroupMembersInfoFunc(ctx, groupID, userIDs, func(ctx context.Context, dbKeys []string) ([]*model_struct.LocalGroupMember, error) {
+ if len(dbKeys) == 0 {
+ return nil, nil
+ }
+ dbData, err := g.db.GetGroupSomeMemberInfo(ctx, groupID, dbKeys)
+ if err != nil {
+ return nil, err
+ }
+ queryKeys := datautil.SliceSubAny(dbKeys, dbData, func(t *model_struct.LocalGroupMember) string {
+ return t.UserID
+ })
+ if len(queryKeys) != 0 {
+ queryData, err := g.GetDesignatedGroupMembers(ctx, groupID, queryKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ dbData = append(dbData, datautil.Batch(ServerGroupMemberToLocalGroupMember, queryData)...)
+ }
+ return dbData, nil
+ })
+}
diff --git a/internal/group/full_sync.go b/internal/group/full_sync.go
new file mode 100644
index 000000000..92d5cee94
--- /dev/null
+++ b/internal/group/full_sync.go
@@ -0,0 +1,124 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package group
+
+import (
+ "context"
+
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+func (g *Group) SyncAllJoinedGroupsAndMembers(ctx context.Context) error {
+ if err := g.IncrSyncJoinGroup(ctx); err != nil {
+ return err
+ }
+ return g.IncrSyncJoinGroupMember(ctx)
+}
+
+func (g *Group) SyncAllSelfGroupApplication(ctx context.Context) error {
+ if !g.groupRequestSyncerLock.TryLock() {
+ return nil
+ }
+ defer g.groupRequestSyncerLock.Unlock()
+ list, err := g.GetServerSelfGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ localData, err := g.db.GetSendGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ if err := g.groupRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalGroupRequest, list), localData, nil); err != nil {
+ return err
+ }
+ // todo
+ return nil
+}
+
+func (g *Group) SyncAllSelfGroupApplicationWithoutNotice(ctx context.Context) error {
+ if !g.groupRequestSyncerLock.TryLock() {
+ return nil
+ }
+ defer g.groupRequestSyncerLock.Unlock()
+ list, err := g.GetServerSelfGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ localData, err := g.db.GetSendGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ if err := g.groupRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalGroupRequest, list), localData, nil, false, true); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Group) SyncSelfGroupApplications(ctx context.Context, groupIDs ...string) error {
+ return g.SyncAllSelfGroupApplication(ctx)
+}
+
+func (g *Group) SyncAllAdminGroupApplication(ctx context.Context) error {
+ if !g.groupAdminRequestSyncerLock.TryLock() {
+ return nil
+ }
+ defer g.groupAdminRequestSyncerLock.Unlock()
+ requests, err := g.GetServerAdminGroupApplicationList(ctx)
+ if err != nil {
+ return err
+ }
+ localData, err := g.db.GetAdminGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return g.groupAdminRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalAdminGroupRequest, requests), localData, nil)
+}
+
+func (g *Group) SyncAllAdminGroupApplicationWithoutNotice(ctx context.Context) error {
+ if !g.groupAdminRequestSyncerLock.TryLock() {
+ return nil
+ }
+ defer g.groupAdminRequestSyncerLock.Unlock()
+ requests, err := g.GetServerAdminGroupApplicationList(ctx)
+ if err != nil {
+ return err
+ }
+ localData, err := g.db.GetAdminGroupApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return g.groupAdminRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalAdminGroupRequest, requests), localData, nil, false, true)
+}
+
+func (g *Group) SyncAdminGroupApplications(ctx context.Context, groupIDs ...string) error {
+ return g.SyncAllAdminGroupApplication(ctx)
+}
+
+func (g *Group) GetServerJoinGroup(ctx context.Context) ([]*sdkws.GroupInfo, error) {
+ return g.getServerJoinGroup(ctx)
+}
+
+func (g *Group) GetServerAdminGroupApplicationList(ctx context.Context) ([]*sdkws.GroupRequest, error) {
+ return g.getServerAdminGroupApplicationList(ctx)
+}
+
+func (g *Group) GetServerSelfGroupApplication(ctx context.Context) ([]*sdkws.GroupRequest, error) {
+ return g.getServerSelfGroupApplication(ctx)
+}
+
+func (g *Group) GetDesignatedGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
+ return g.getDesignatedGroupMembers(ctx, groupID, userIDs)
+}
diff --git a/internal/group/group.go b/internal/group/group.go
index 5b25db979..007603cd5 100644
--- a/internal/group/group.go
+++ b/internal/group/group.go
@@ -18,10 +18,12 @@ import (
"context"
"sync"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/cache"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/datafetcher"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/page"
@@ -47,10 +49,10 @@ func NewGroup(loginUserID string, db db_interface.DataBase,
conversationCh: conversationCh,
}
g.initSyncer()
+ g.groupMemberCache = cache.NewCache[string, *model_struct.LocalGroupMember]()
return g
}
-// //utils.GetCurrentTimestampByMill()
type Group struct {
listener func() open_im_sdk_callback.OnGroupListener
loginUserID string
@@ -59,14 +61,17 @@ type Group struct {
groupMemberSyncer *syncer.Syncer[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string]
groupRequestSyncer *syncer.Syncer[*model_struct.LocalGroupRequest, syncer.NoResp, [2]string]
groupAdminRequestSyncer *syncer.Syncer[*model_struct.LocalAdminGroupRequest, syncer.NoResp, [2]string]
- joinedSuperGroupCh chan common.Cmd2Value
- heartbeatCmdCh chan common.Cmd2Value
conversationCh chan common.Cmd2Value
// memberSyncMutex sync.RWMutex
groupSyncMutex sync.Mutex
listenerForService open_im_sdk_callback.OnListenerForService
+
+ groupMemberCache *cache.Cache[string, *model_struct.LocalGroupMember]
+
+ groupRequestSyncerLock sync.Mutex
+ groupAdminRequestSyncerLock sync.Mutex
}
func (g *Group) initSyncer() {
@@ -99,7 +104,7 @@ func (g *Group) initSyncer() {
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{
Action: constant.UpdateConFaceUrlAndNickName,
Args: common.SourceIDAndSessionType{
- SourceID: server.GroupID, SessionType: constant.SuperGroupChatType,
+ SourceID: server.GroupID, SessionType: constant.ReadGroupChatType,
FaceURL: server.FaceURL, Nickname: server.GroupName,
},
}, g.conversationCh)
@@ -120,7 +125,7 @@ func (g *Group) initSyncer() {
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{
Action: constant.UpdateConFaceUrlAndNickName,
Args: common.SourceIDAndSessionType{
- SourceID: server.GroupID, SessionType: constant.SuperGroupChatType,
+ SourceID: server.GroupID, SessionType: constant.ReadGroupChatType,
FaceURL: server.FaceURL, Nickname: server.GroupName,
},
}, g.conversationCh)
@@ -129,6 +134,7 @@ func (g *Group) initSyncer() {
}
return nil
}),
+
syncer.WithBatchInsert[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, values []*model_struct.LocalGroup) error {
return g.db.BatchInsertGroup(ctx, values)
}),
@@ -142,10 +148,9 @@ func (g *Group) initSyncer() {
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(resp *group.GetJoinedGroupListResp) []*model_struct.LocalGroup {
return datautil.Batch(ServerGroupToLocalGroup, resp.Groups)
}),
- syncer.WithReqApiRouter[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](constant.GetJoinedGroupListRouter),
+ syncer.WithReqApiRouter[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](api.GetJoinedGroupList.Route()),
syncer.WithFullSyncLimit[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](groupSyncLimit),
)
-
g.groupMemberSyncer = syncer.New2[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](
syncer.WithInsert[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, value *model_struct.LocalGroupMember) error {
return g.db.InsertGroupMember(ctx, value)
@@ -154,6 +159,7 @@ func (g *Group) initSyncer() {
return g.db.DeleteGroupMember(ctx, value.GroupID, value.UserID)
}),
syncer.WithUpdate[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, server, local *model_struct.LocalGroupMember) error {
+ g.groupMemberCache.Delete(g.buildGroupMemberKey(server.GroupID, server.UserID))
return g.db.UpdateGroupMember(ctx, server)
}),
syncer.WithUUID[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(value *model_struct.LocalGroupMember) [2]string {
@@ -168,7 +174,7 @@ func (g *Group) initSyncer() {
common.UpdateMessageNode{
Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{
- SessionType: constant.SuperGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
+ SessionType: constant.ReadGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
Nickname: server.Nickname, GroupID: server.GroupID,
},
}, g.conversationCh)
@@ -181,10 +187,14 @@ func (g *Group) initSyncer() {
common.UpdateMessageNode{
Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{
- SessionType: constant.SuperGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
+ SessionType: constant.ReadGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
Nickname: server.Nickname, GroupID: server.GroupID,
},
}, g.conversationCh)
+ _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.UpdateLatestMessageFaceUrlAndNickName, Args: common.UpdateMessageInfo{
+ SessionType: constant.ReadGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
+ Nickname: server.Nickname, GroupID: server.GroupID,
+ }}, g.conversationCh)
}
}
return nil
@@ -201,7 +211,7 @@ func (g *Group) initSyncer() {
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(resp *group.GetGroupMemberListResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Members)
}),
- syncer.WithReqApiRouter[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](constant.GetGroupMemberListRouter),
+ syncer.WithReqApiRouter[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](api.GetGroupMemberList.Route()),
syncer.WithFullSyncLimit[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](groupMemberSyncLimit),
)
@@ -265,89 +275,39 @@ func (g *Group) SetListenerForService(listener open_im_sdk_callback.OnListenerFo
g.listenerForService = listener
}
-func (g *Group) GetGroupOwnerIDAndAdminIDList(ctx context.Context, groupID string) (ownerID string, adminIDList []string, err error) {
- localGroup, err := g.db.GetGroupInfoByGroupID(ctx, groupID)
- if err != nil {
- return "", nil, err
- }
- adminIDList, err = g.db.GetGroupAdminID(ctx, groupID)
- if err != nil {
- return "", nil, err
- }
- return localGroup.OwnerUserID, adminIDList, nil
-}
-
-func (g *Group) GetGroupInfoFromLocal2Svr(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
- localGroup, err := g.db.GetGroupInfoByGroupID(ctx, groupID)
- if err == nil {
- return localGroup, nil
- }
- svrGroup, err := g.getGroupsInfoFromSvr(ctx, []string{groupID})
- if err != nil {
- return nil, err
- }
- if len(svrGroup) == 0 {
- return nil, sdkerrs.ErrGroupIDNotFound.WrapMsg("server not this group")
- }
- return ServerGroupToLocalGroup(svrGroup[0]), nil
-}
-
-func (g *Group) GetGroupsInfoFromLocal2Svr(ctx context.Context, groupIDs ...string) (map[string]*model_struct.LocalGroup, error) {
- groupMap := make(map[string]*model_struct.LocalGroup)
- if len(groupIDs) == 0 {
- return groupMap, nil
- }
- groups, err := g.db.GetGroups(ctx, groupIDs)
+func (g *Group) FetchGroupOrError(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
+ dataFetcher := datafetcher.NewDataFetcher(
+ g.db,
+ g.groupTableName(),
+ g.loginUserID,
+ func(localGroup *model_struct.LocalGroup) string {
+ return localGroup.GroupID
+ },
+ func(ctx context.Context, values []*model_struct.LocalGroup) error {
+ return g.db.BatchInsertGroup(ctx, values)
+ },
+ func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, bool, error) {
+ localGroups, err := g.db.GetGroups(ctx, groupIDs)
+ return localGroups, true, err
+ },
+ func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
+ serverGroupInfo, err := g.getGroupsInfoFromServer(ctx, groupIDs)
+ if err != nil {
+ return nil, err
+ }
+ return datautil.Batch(ServerGroupToLocalGroup, serverGroupInfo), nil
+ },
+ )
+ groups, err := dataFetcher.FetchMissingAndCombineLocal(ctx, []string{groupID})
if err != nil {
return nil, err
}
- var groupIDsNeedSync []string
- localGroupIDs := datautil.Slice(groups, func(group *model_struct.LocalGroup) string {
- return group.GroupID
- })
- for _, groupID := range groupIDs {
- if !datautil.Contain(groupID, localGroupIDs...) {
- groupIDsNeedSync = append(groupIDsNeedSync, groupID)
- }
- }
-
- if len(groupIDsNeedSync) > 0 {
- svrGroups, err := g.getGroupsInfoFromSvr(ctx, groupIDsNeedSync)
- if err != nil {
- return nil, err
- }
- for _, svrGroup := range svrGroups {
- groups = append(groups, ServerGroupToLocalGroup(svrGroup))
- }
- }
- for _, group := range groups {
- groupMap[group.GroupID] = group
- }
- return groupMap, nil
-}
-
-func (g *Group) getGroupsInfoFromSvr(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
- resp, err := util.CallApi[group.GetGroupsInfoResp](ctx, constant.GetGroupsInfoRouter, &group.GetGroupsInfoReq{GroupIDs: groupIDs})
- if err != nil {
- return nil, err
+ if len(groups) == 0 {
+ return nil, sdkerrs.ErrGroupIDNotFound.WrapMsg("sdk and server not this group")
}
- return resp.GroupInfos, nil
-}
-
-func (g *Group) getGroupAbstractInfoFromSvr(ctx context.Context, groupIDs []string) (*group.GetGroupAbstractInfoResp, error) {
- return util.CallApi[group.GetGroupAbstractInfoResp](ctx, constant.GetGroupAbstractInfoRouter, &group.GetGroupAbstractInfoReq{GroupIDs: groupIDs})
+ return groups[0], nil
}
-func (g *Group) GetJoinedDiffusionGroupIDListFromSvr(ctx context.Context) ([]string, error) {
- groups, err := g.GetServerJoinGroup(ctx)
- if err != nil {
- return nil, err
- }
- var groupIDs []string
- for _, g := range groups {
- if g.GroupType == constant.WorkingGroup {
- groupIDs = append(groupIDs, g.GroupID)
- }
- }
- return groupIDs, nil
+func (g *Group) delLocalGroupRequest(ctx context.Context, groupID, userID string) error {
+ return g.db.DeleteGroupRequest(ctx, groupID, userID)
}
diff --git a/internal/group/sync2.go b/internal/group/incremental_sync.go
similarity index 80%
rename from internal/group/sync2.go
rename to internal/group/incremental_sync.go
index b53fb7829..c01e241f4 100644
--- a/internal/group/sync2.go
+++ b/internal/group/incremental_sync.go
@@ -4,10 +4,8 @@ import (
"context"
"sync"
- "github.com/openimsdk/openim-sdk-core/v3/internal/incrversion"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
constantpb "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
@@ -16,21 +14,13 @@ import (
"github.com/openimsdk/tools/utils/datautil"
)
-type BatchIncrementalReq struct {
- UserID string `json:"user_id"`
- List []*group.GetIncrementalGroupMemberReq `json:"list"`
-}
-type BatchIncrementalResp struct {
- List map[string]*group.GetIncrementalGroupMemberResp `json:"list"`
-}
-
-func (g *Group) getIncrementalGroupMemberBatch(ctx context.Context, groups []*group.GetIncrementalGroupMemberReq) (map[string]*group.GetIncrementalGroupMemberResp, error) {
- resp, err := util.CallApi[BatchIncrementalResp](ctx, constant.GetIncrementalGroupMemberBatch, &BatchIncrementalReq{UserID: g.loginUserID, List: groups})
- if err != nil {
- return nil, err
- }
- return resp.List, nil
-}
+//func (g *Group) GetIncrementalGroupMemberBatch(ctx context.Context, groups []*group.GetIncrementalGroupMemberReq) (map[string]*group.GetIncrementalGroupMemberResp, error) {
+// resp, err := g.getIncrementalGroupMemberBatch(ctx, &group.BatchGetIncrementalGroupMemberReq{UserID: g.loginUserID, ReqList: groups})
+// if err != nil {
+// return nil, err
+// }
+// return resp.RespList, nil
+//}
func (g *Group) groupAndMemberVersionTableName() string {
return "local_group_entities_version"
@@ -80,7 +70,7 @@ func (g *Group) IncrSyncGroupAndMember(ctx context.Context, groupIDs ...string)
if err == nil {
req.VersionID = lvs.VersionID
req.Version = lvs.Version
- } else if errs.Unwrap(err) != errs.ErrRecordNotFound {
+ } else if !errs.ErrRecordNotFound.Is(err) {
return err
}
groups = append(groups, &req)
@@ -105,13 +95,11 @@ func (g *Group) IncrSyncGroupAndMember(ctx context.Context, groupIDs ...string)
delete(groupIDSet, tempGroupID)
}
wg.Wait()
- num := len(groupIDSet)
- _ = num
}
}
func (g *Group) syncGroupAndMember(ctx context.Context, groupID string, resp *group.GetIncrementalGroupMemberResp) error {
- groupMemberSyncer := incrversion.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
+ groupMemberSyncer := syncer.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupAndMemberVersionTableName(),
@@ -174,7 +162,7 @@ func (g *Group) syncGroupAndMember(ctx context.Context, groupID string, resp *gr
return g.groupMemberSyncer.FullSync(ctx, groupID)
},
FullID: func(ctx context.Context) ([]string, error) {
- resp, err := util.CallApi[group.GetFullGroupMemberUserIDsResp](ctx, constant.GetFullGroupMemberUserIDs, &group.GetFullGroupMemberUserIDsReq{
+ resp, err := g.getFullGroupMemberUserIDs(ctx, &group.GetFullGroupMemberUserIDsReq{
GroupID: groupID,
})
if err != nil {
@@ -182,13 +170,19 @@ func (g *Group) syncGroupAndMember(ctx context.Context, groupID string, resp *gr
}
return resp.UserIDs, nil
},
+ IDOrderChanged: func(resp *group.GetIncrementalGroupMemberResp) bool {
+ if resp.SortVersion > 0 {
+ return true
+ }
+ return false
+ },
}
- return groupMemberSyncer.Sync()
+ return groupMemberSyncer.IncrementalSync()
}
func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, deleteGroupMembers, updateGroupMembers, insertGroupMembers []*sdkws.GroupMemberFullInfo,
- updateGroup *sdkws.GroupInfo, version uint64, versionID string) error {
- groupMemberSyncer := incrversion.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
+ updateGroup *sdkws.GroupInfo, sortVersion uint64, version uint64, versionID string) error {
+ groupMemberSyncer := syncer.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupAndMemberVersionTableName(),
@@ -207,9 +201,10 @@ func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, de
Delete: datautil.Slice(deleteGroupMembers, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
}),
- Insert: insertGroupMembers,
- Update: updateGroupMembers,
- Group: updateGroup,
+ Insert: insertGroupMembers,
+ Update: updateGroupMembers,
+ Group: updateGroup,
+ SortVersion: sortVersion,
}
},
Server: func(version *model_struct.LocalVersionSync) (*group.GetIncrementalGroupMemberResp, error) {
@@ -218,13 +213,13 @@ func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, de
VersionID: version.VersionID,
Version: version.Version,
}
- resp, err := util.CallApi[BatchIncrementalResp](ctx, constant.GetIncrementalGroupMemberBatch,
- &BatchIncrementalReq{UserID: g.loginUserID, List: []*group.GetIncrementalGroupMemberReq{singleGroupReq}})
+
+ resp, err := g.getIncrementalGroupMemberBatch(ctx, []*group.GetIncrementalGroupMemberReq{singleGroupReq})
if err != nil {
return nil, err
}
- if resp.List != nil {
- if singleGroupResp, ok := resp.List[groupID]; ok {
+ if resp != nil {
+ if singleGroupResp, ok := resp[groupID]; ok {
return singleGroupResp, nil
}
}
@@ -280,7 +275,7 @@ func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, de
return g.groupMemberSyncer.FullSync(ctx, groupID)
},
FullID: func(ctx context.Context) ([]string, error) {
- resp, err := util.CallApi[group.GetFullGroupMemberUserIDsResp](ctx, constant.GetFullGroupMemberUserIDs, &group.GetFullGroupMemberUserIDsReq{
+ resp, err := g.getFullGroupMemberUserIDs(ctx, &group.GetFullGroupMemberUserIDsReq{
GroupID: groupID,
})
if err != nil {
@@ -288,12 +283,18 @@ func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, de
}
return resp.UserIDs, nil
},
+ IDOrderChanged: func(resp *group.GetIncrementalGroupMemberResp) bool {
+ if resp.SortVersion > 0 {
+ return true
+ }
+ return false
+ },
}
return groupMemberSyncer.CheckVersionSync()
}
func (g *Group) IncrSyncJoinGroup(ctx context.Context) error {
- opt := incrversion.VersionSynchronizer[*model_struct.LocalGroup, *group.GetIncrementalJoinGroupResp]{
+ joinedGroupSyncer := syncer.VersionSynchronizer[*model_struct.LocalGroup, *group.GetIncrementalJoinGroupResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupTableName(),
@@ -305,7 +306,7 @@ func (g *Group) IncrSyncJoinGroup(ctx context.Context) error {
return g.db.GetJoinedGroupListDB(ctx)
},
Server: func(version *model_struct.LocalVersionSync) (*group.GetIncrementalJoinGroupResp, error) {
- return util.CallApi[group.GetIncrementalJoinGroupResp](ctx, constant.GetIncrementalJoinGroup, &group.GetIncrementalJoinGroupReq{
+ return g.getIncrementalJoinGroup(ctx, &group.GetIncrementalJoinGroupReq{
UserID: g.loginUserID,
Version: version.Version,
VersionID: version.VersionID,
@@ -333,7 +334,7 @@ func (g *Group) IncrSyncJoinGroup(ctx context.Context) error {
return g.groupSyncer.FullSync(ctx, g.loginUserID)
},
FullID: func(ctx context.Context) ([]string, error) {
- resp, err := util.CallApi[group.GetFullJoinGroupIDsResp](ctx, constant.GetFullJoinedGroupIDs, &group.GetFullJoinGroupIDsReq{
+ resp, err := g.getFullJoinGroupIDs(ctx, &group.GetFullJoinGroupIDsReq{
UserID: g.loginUserID,
})
if err != nil {
@@ -342,6 +343,12 @@ func (g *Group) IncrSyncJoinGroup(ctx context.Context) error {
return resp.GroupIDs, nil
},
+ IDOrderChanged: func(resp *group.GetIncrementalJoinGroupResp) bool {
+ if resp.SortVersion > 0 {
+ return true
+ }
+ return false
+ },
}
- return opt.Sync()
+ return joinedGroupSyncer.IncrementalSync()
}
diff --git a/internal/group/notification.go b/internal/group/notification.go
index 5540643e5..64fca4219 100644
--- a/internal/group/notification.go
+++ b/internal/group/notification.go
@@ -17,7 +17,6 @@ package group
import (
"context"
"fmt"
-
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/tools/errs"
@@ -27,10 +26,13 @@ import (
"github.com/openimsdk/tools/log"
)
-func (g *Group) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
+const (
+ groupSortIDUnchanged = 0
+ groupSortIDChanged = 1
+)
+func (g *Group) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
go func() {
-
if err := g.doNotification(ctx, msg); err != nil {
log.ZError(ctx, "DoGroupNotification failed", err)
}
@@ -38,48 +40,7 @@ func (g *Group) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
}
func (g *Group) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
- g.groupSyncMutex.Lock()
- defer g.groupSyncMutex.Unlock()
switch msg.ContentType {
- case constant.GroupCreatedNotification: // 1501
- var detail sdkws.GroupCreatedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
-
- if err := g.IncrSyncJoinGroup(ctx); err != nil {
- return err
- }
- return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
-
- case constant.GroupInfoSetNotification: // 1502
- var detail sdkws.GroupInfoSetTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.JoinGroupApplicationNotification: // 1503
- var detail sdkws.JoinGroupApplicationTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- if detail.Applicant.UserID == g.loginUserID {
- return g.SyncSelfGroupApplications(ctx, detail.Group.GroupID)
- } else {
- return g.SyncAdminGroupApplications(ctx, detail.Group.GroupID)
- }
- case constant.MemberQuitNotification: // 1504
- var detail sdkws.MemberQuitTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- if detail.QuitUser.UserID == g.loginUserID {
- return g.IncrSyncJoinGroup(ctx)
- } else {
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, []*sdkws.GroupMemberFullInfo{detail.QuitUser},
- nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- }
case constant.GroupApplicationAcceptedNotification: // 1505
var detail sdkws.GroupApplicationAcceptedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
@@ -93,6 +54,7 @@ func (g *Group) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
default:
return errs.New(fmt.Sprintf("GroupApplicationAcceptedNotification ReceiverAs unknown %d", detail.ReceiverAs)).Wrap()
}
+
case constant.GroupApplicationRejectedNotification: // 1506
var detail sdkws.GroupApplicationRejectedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
@@ -106,145 +68,207 @@ func (g *Group) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
default:
return errs.New(fmt.Sprintf("GroupApplicationRejectedNotification ReceiverAs unknown %d", detail.ReceiverAs)).Wrap()
}
- case constant.GroupOwnerTransferredNotification: // 1507
- var detail sdkws.GroupOwnerTransferredTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- if detail.Group == nil {
- return errs.New(fmt.Sprintf("group is nil, groupID: %s", detail.Group.GroupID)).Wrap()
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.NewGroupOwner, detail.OldGroupOwnerInfo}, nil,
- detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.MemberKickedNotification: // 1508
- var detail sdkws.MemberKickedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- var self bool
- for _, info := range detail.KickedUserList {
- if info.UserID == g.loginUserID {
- self = true
- break
+
+ default:
+ g.groupSyncMutex.Lock()
+ defer g.groupSyncMutex.Unlock()
+ switch msg.ContentType {
+ case constant.GroupCreatedNotification: // 1501
+ var detail sdkws.GroupCreatedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
}
- }
- if self {
- return g.IncrSyncJoinGroup(ctx)
- } else {
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, detail.KickedUserList, nil,
- nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- }
- case constant.MemberInvitedNotification: // 1509
- var detail sdkws.MemberInvitedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- userIDMap := datautil.SliceSetAny(detail.InvitedUserList, func(e *sdkws.GroupMemberFullInfo) string {
- return e.UserID
- })
- //自己也是被邀请的一员
- if _, ok := userIDMap[g.loginUserID]; ok {
+
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
- } else {
+
+ case constant.GroupInfoSetNotification: // 1502
+ var detail sdkws.GroupInfoSetTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ nil, nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.JoinGroupApplicationNotification: // 1503
+ var detail sdkws.JoinGroupApplicationTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.Applicant.UserID == g.loginUserID {
+ return g.SyncSelfGroupApplications(ctx, detail.Group.GroupID)
+ } else {
+ return g.SyncAdminGroupApplications(ctx, detail.Group.GroupID)
+ }
+ case constant.MemberQuitNotification: // 1504
+ var detail sdkws.MemberQuitTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.QuitUser.UserID == g.loginUserID {
+ return g.IncrSyncJoinGroup(ctx)
+ } else {
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, []*sdkws.GroupMemberFullInfo{detail.QuitUser}, nil,
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ }
+ case constant.GroupOwnerTransferredNotification: // 1507
+ var detail sdkws.GroupOwnerTransferredTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.Group == nil {
+ return errs.New(fmt.Sprintf("group is nil, groupID: %s", detail.Group.GroupID)).Wrap()
+ }
+ if detail.NewGroupOwner.RoleLevel < constant.GroupAdmin && detail.OldGroupOwner == g.loginUserID {
+ if err := g.delLocalGroupRequest(ctx, detail.Group.GroupID, g.loginUserID); err != nil {
+ return err
+ }
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.NewGroupOwner, detail.OldGroupOwnerInfo}, nil,
+ detail.Group, groupSortIDChanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.MemberKickedNotification: // 1508
+ var detail sdkws.MemberKickedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ var self bool
+ for _, info := range detail.KickedUserList {
+ if info.UserID == g.loginUserID {
+ self = true
+ break
+ }
+ }
+ if self {
+ if err := g.delLocalGroupRequest(ctx, detail.Group.GroupID, g.loginUserID); err != nil {
+ return err
+ }
+ return g.IncrSyncJoinGroup(ctx)
+ } else {
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, detail.KickedUserList, nil,
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ }
+ case constant.MemberInvitedNotification: // 1509
+ var detail sdkws.MemberInvitedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ userIDMap := datautil.SliceSetAny(detail.InvitedUserList, func(e *sdkws.GroupMemberFullInfo) string {
+ return e.UserID
+ })
+ //Also invited as a member
+ if _, ok := userIDMap[g.loginUserID]; ok {
+ if err := g.IncrSyncJoinGroup(ctx); err != nil {
+ return err
+ }
+ return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
+ } else {
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
+ detail.InvitedUserList, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ }
+ case constant.MemberEnterNotification: // 1510
+ var detail sdkws.MemberEnterTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.EntrantUser.UserID == g.loginUserID {
+ if err := g.IncrSyncJoinGroup(ctx); err != nil {
+ return err
+ }
+ return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
+ } else {
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
+ []*sdkws.GroupMemberFullInfo{detail.EntrantUser}, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ }
+ case constant.GroupDismissedNotification: // 1511
+ var detail sdkws.GroupDismissedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ g.listener().OnGroupDismissed(utils.StructToJsonString(detail.Group))
+
+ return g.IncrSyncJoinGroup(ctx)
+ case constant.GroupMemberMutedNotification: // 1512
+ var detail sdkws.GroupMemberMutedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
+ groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupMemberCancelMutedNotification: // 1513
+ var detail sdkws.GroupMemberCancelMutedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
+ groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupMutedNotification: // 1514
+ var detail sdkws.GroupMutedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
- detail.InvitedUserList, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- }
- case constant.MemberEnterNotification: // 1510
- var detail sdkws.MemberEnterTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- if detail.EntrantUser.UserID == g.loginUserID {
- if err := g.IncrSyncJoinGroup(ctx); err != nil {
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupCancelMutedNotification: // 1515
+ var detail sdkws.GroupCancelMutedTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
- return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
- } else {
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
- []*sdkws.GroupMemberFullInfo{detail.EntrantUser}, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- }
- case constant.GroupDismissedNotification: // 1511
- var detail sdkws.GroupDismissedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- g.listener().OnGroupDismissed(utils.StructToJsonString(detail.Group))
-
- return g.IncrSyncJoinGroup(ctx)
- case constant.GroupMemberMutedNotification: // 1512
- var detail sdkws.GroupMemberMutedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
- detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupMemberCancelMutedNotification: // 1513
- var detail sdkws.GroupMemberCancelMutedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
- detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupMutedNotification: // 1514
- var detail sdkws.GroupMutedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
- nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupCancelMutedNotification: // 1515
- var detail sdkws.GroupCancelMutedTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
- nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupMemberInfoSetNotification: // 1516
- var detail sdkws.GroupMemberInfoSetTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
-
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
- detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupMemberSetToAdminNotification: // 1517
- var detail sdkws.GroupMemberInfoSetTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
- detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupMemberSetToOrdinaryUserNotification: // 1518
- var detail sdkws.GroupMemberInfoSetTips
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
- detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupInfoSetAnnouncementNotification: // 1519
- var detail sdkws.GroupInfoSetAnnouncementTips //
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
- }
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
- nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- case constant.GroupInfoSetNameNotification: // 1520
- var detail sdkws.GroupInfoSetNameTips //
- if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
- return err
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupMemberInfoSetNotification: // 1516
+ var detail sdkws.GroupMemberInfoSetTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.ChangedUser.RoleLevel < constant.GroupAdmin && detail.ChangedUser.UserID == g.loginUserID {
+ if err := g.delLocalGroupRequest(ctx, detail.Group.GroupID, g.loginUserID); err != nil {
+ return err
+ }
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
+ detail.GroupSortVersion, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupMemberSetToAdminNotification: // 1517
+ var detail sdkws.GroupMemberInfoSetTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
+ detail.GroupSortVersion, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupMemberSetToOrdinaryUserNotification: // 1518
+ var detail sdkws.GroupMemberInfoSetTips
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ if detail.ChangedUser.UserID == g.loginUserID {
+ if err := g.delLocalGroupRequest(ctx, detail.Group.GroupID, g.loginUserID); err != nil {
+ return err
+ }
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
+ []*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
+ detail.GroupSortVersion, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupInfoSetAnnouncementNotification: // 1519
+ var detail sdkws.GroupInfoSetAnnouncementTips //
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ case constant.GroupInfoSetNameNotification: // 1520
+ var detail sdkws.GroupInfoSetNameTips //
+ if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
+ return err
+ }
+ return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
+ nil, detail.Group, groupSortIDUnchanged, detail.GroupMemberVersion, detail.GroupMemberVersionID)
+ default:
+ return errs.New("unknown tips type", "contentType", msg.ContentType).Wrap()
}
- return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
- nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
- default:
- return errs.New("unknown tips type", "contentType", msg.ContentType).Wrap()
}
}
diff --git a/internal/group/server_api.go b/internal/group/server_api.go
new file mode 100644
index 000000000..bcff10add
--- /dev/null
+++ b/internal/group/server_api.go
@@ -0,0 +1,107 @@
+package group
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+)
+
+func (g *Group) getFullGroupMemberUserIDs(ctx context.Context, req *group.GetFullGroupMemberUserIDsReq) (*group.GetFullGroupMemberUserIDsResp, error) {
+ return api.GetFullGroupMemberUserIDs.Invoke(ctx, req)
+}
+
+func (g *Group) getIncrementalJoinGroup(ctx context.Context, req *group.GetIncrementalJoinGroupReq) (*group.GetIncrementalJoinGroupResp, error) {
+ return api.GetIncrementalJoinGroup.Invoke(ctx, req)
+}
+
+func (g *Group) getFullJoinGroupIDs(ctx context.Context, req *group.GetFullJoinGroupIDsReq) (*group.GetFullJoinGroupIDsResp, error) {
+ return api.GetFullJoinedGroupIDs.Invoke(ctx, req)
+}
+
+func (g *Group) getIncrementalGroupMemberBatch(ctx context.Context, reqs []*group.GetIncrementalGroupMemberReq) (map[string]*group.GetIncrementalGroupMemberResp, error) {
+ req := &group.BatchGetIncrementalGroupMemberReq{UserID: g.loginUserID, ReqList: reqs}
+ return api.ExtractField(ctx, api.GetIncrementalGroupMemberBatch.Invoke, req, (*group.BatchGetIncrementalGroupMemberResp).GetRespList)
+}
+
+func (g *Group) createGroup(ctx context.Context, req *group.CreateGroupReq) (*group.CreateGroupResp, error) {
+ return api.CreateGroup.Invoke(ctx, req)
+}
+
+func (g *Group) joinGroup(ctx context.Context, req *group.JoinGroupReq) error {
+ return api.JoinGroup.Execute(ctx, req)
+}
+
+func (g *Group) quitGroup(ctx context.Context, groupID string) error {
+ return api.QuitGroup.Execute(ctx, &group.QuitGroupReq{GroupID: groupID, UserID: g.loginUserID})
+}
+
+func (g *Group) dismissGroup(ctx context.Context, groupID string) error {
+ return api.DismissGroup.Execute(ctx, &group.DismissGroupReq{GroupID: groupID})
+}
+
+func (g *Group) setGroupInfo(ctx context.Context, req *group.SetGroupInfoExReq) error {
+ return api.SetGroupInfoEx.Execute(ctx, req)
+}
+
+func (g *Group) setGroupMemberInfo(ctx context.Context, req *group.SetGroupMemberInfoReq) error {
+ return api.SetGroupMemberInfo.Execute(ctx, req)
+}
+
+func (g *Group) kickGroupMember(ctx context.Context, req *group.KickGroupMemberReq) error {
+ return api.KickGroupMember.Execute(ctx, req)
+}
+
+func (g *Group) transferGroup(ctx context.Context, req *group.TransferGroupOwnerReq) error {
+ return api.TransferGroup.Execute(ctx, req)
+}
+
+func (g *Group) cancelMuteGroupMember(ctx context.Context, req *group.CancelMuteGroupMemberReq) error {
+ return api.CancelMuteGroupMember.Execute(ctx, req)
+}
+
+func (g *Group) muteGroupMember(ctx context.Context, req *group.MuteGroupMemberReq) error {
+ return api.MuteGroupMember.Execute(ctx, req)
+}
+
+func (g *Group) muteGroup(ctx context.Context, groupID string) error {
+ return api.MuteGroup.Execute(ctx, &group.MuteGroupReq{GroupID: groupID})
+}
+
+func (g *Group) cancelMuteGroup(ctx context.Context, groupID string) error {
+ return api.CancelMuteGroup.Execute(ctx, &group.CancelMuteGroupReq{GroupID: groupID})
+}
+
+func (g *Group) getDesignatedGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
+ req := &group.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userIDs}
+ return api.ExtractField(ctx, api.GetGroupMembersInfo.Invoke, req, (*group.GetGroupMembersInfoResp).GetMembers)
+}
+
+func (g *Group) getServerSelfGroupApplication(ctx context.Context) ([]*sdkws.GroupRequest, error) {
+ req := &group.GetUserReqApplicationListReq{UserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ return api.Page(ctx, req, api.GetSendGroupApplicationList.Invoke, (*group.GetUserReqApplicationListResp).GetGroupRequests)
+}
+
+func (g *Group) getServerJoinGroup(ctx context.Context) ([]*sdkws.GroupInfo, error) {
+ req := &group.GetJoinedGroupListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ return api.Page(ctx, req, api.GetJoinedGroupList.Invoke, (*group.GetJoinedGroupListResp).GetGroups)
+}
+
+func (g *Group) getServerAdminGroupApplicationList(ctx context.Context) ([]*sdkws.GroupRequest, error) {
+ req := &group.GetGroupApplicationListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ return api.Page(ctx, req, api.GetRecvGroupApplicationList.Invoke, (*group.GetGroupApplicationListResp).GetGroupRequests)
+}
+
+func (g *Group) getGroupsInfoFromServer(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
+ req := &group.GetGroupsInfoReq{GroupIDs: groupIDs}
+ return api.ExtractField(ctx, api.GetGroupsInfo.Invoke, req, (*group.GetGroupsInfoResp).GetGroupInfos)
+}
+
+func (g *Group) inviteUserToGroup(ctx context.Context, req *group.InviteUserToGroupReq) error {
+ return api.InviteUserToGroup.Execute(ctx, req)
+}
+
+func (g *Group) handlerGroupApplication(ctx context.Context, req *group.GroupApplicationResponseReq) error {
+ return api.AcceptGroupApplication.Execute(ctx, req)
+}
diff --git a/internal/group/sync.go b/internal/group/sync.go
deleted file mode 100644
index 404ed7f44..000000000
--- a/internal/group/sync.go
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package group
-
-import (
- "context"
- "crypto/md5"
- "encoding/binary"
- "encoding/json"
- "errors"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/protocol/group"
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
- "github.com/openimsdk/tools/utils/datautil"
-)
-
-func (g *Group) getGroupHash(members []*model_struct.LocalGroupMember) uint64 {
- userIDs := datautil.Slice(members, func(member *model_struct.LocalGroupMember) string {
- return member.UserID
- })
- datautil.Sort(userIDs, true)
- memberMap := make(map[string]*sdkws.GroupMemberFullInfo)
- for _, member := range members {
- memberMap[member.UserID] = &sdkws.GroupMemberFullInfo{
- GroupID: member.GroupID,
- UserID: member.UserID,
- RoleLevel: member.RoleLevel,
- JoinTime: member.JoinTime,
- Nickname: member.Nickname,
- FaceURL: member.FaceURL,
- AppMangerLevel: 0,
- JoinSource: member.JoinSource,
- OperatorUserID: member.OperatorUserID,
- Ex: member.Ex,
- MuteEndTime: member.MuteEndTime,
- InviterUserID: member.InviterUserID,
- }
- }
- res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
- for _, userID := range userIDs {
- res = append(res, memberMap[userID])
- }
- val, _ := json.Marshal(res)
- sum := md5.Sum(val)
- return binary.BigEndian.Uint64(sum[:])
-}
-
-func (g *Group) SyncAllGroupMember(ctx context.Context, groupID string) error {
- absInfo, err := g.GetGroupAbstractInfo(ctx, groupID)
- if err != nil {
- return err
- }
- localData, err := g.db.GetGroupMemberListSplit(ctx, groupID, 0, 0, 9999999)
- if err != nil {
- return err
- }
- hashCode := g.getGroupHash(localData)
- if len(localData) == int(absInfo.GroupMemberNumber) && hashCode == absInfo.GroupMemberListHash {
- log.ZDebug(ctx, "SyncAllGroupMember no change in personnel", "groupID", groupID, "hashCode", hashCode, "absInfo.GroupMemberListHash", absInfo.GroupMemberListHash)
- return nil
- }
- members, err := g.GetServerGroupMembers(ctx, groupID)
- if err != nil {
- return err
- }
- return g.syncGroupMembers(ctx, groupID, members, localData)
-}
-
-func (g *Group) syncGroupMembers(ctx context.Context, groupID string, members []*sdkws.GroupMemberFullInfo, localData []*model_struct.LocalGroupMember) error {
- log.ZInfo(ctx, "SyncGroupMember Info", "groupID", groupID, "members", len(members), "localData", len(localData))
- err := g.groupMemberSyncer.Sync(ctx, datautil.Batch(ServerGroupMemberToLocalGroupMember, members), localData, nil)
- if err != nil {
- return err
- }
- //if len(members) != len(localData) {
- log.ZInfo(ctx, "SyncGroupMember Sync Group Member Count", "groupID", groupID, "members", len(members), "localData", len(localData))
- gs, err := g.GetSpecifiedGroupsInfo(ctx, []string{groupID})
- if err != nil {
- return err
- }
- log.ZInfo(ctx, "SyncGroupMember GetGroupsInfo", "groupID", groupID, "len", len(gs), "gs", gs)
- if len(gs) > 0 {
- v := gs[0]
- count, err := g.db.GetGroupMemberCount(ctx, groupID)
- if err != nil {
- return err
- }
- if v.MemberCount != count {
- v.MemberCount = count
- if v.GroupType == constant.SuperGroupChatType {
- if err := g.db.UpdateSuperGroup(ctx, v); err != nil {
- //return err
- log.ZError(ctx, "SyncGroupMember UpdateSuperGroup", err, "groupID", groupID, "info", v)
- }
- } else {
- if err := g.db.UpdateGroup(ctx, v); err != nil {
- log.ZError(ctx, "SyncGroupMember UpdateGroup", err, "groupID", groupID, "info", v)
- }
- }
- data, err := json.Marshal(v)
- if err != nil {
- return err
- }
- log.ZInfo(ctx, "SyncGroupMember OnGroupInfoChanged", "groupID", groupID, "data", string(data))
- g.listener().OnGroupInfoChanged(string(data))
- }
- }
- //}
- return nil
-}
-
-func (g *Group) SyncGroupMembers(ctx context.Context, groupID string, userIDs ...string) error {
- return g.IncrSyncGroupAndMember(ctx, groupID)
-
-}
-
-func (g *Group) SyncAllJoinedGroupsAndMembers(ctx context.Context) error {
- if err := g.IncrSyncJoinGroup(ctx); err != nil {
- return err
- }
- return g.IncrSyncJoinGroupMember(ctx)
-}
-
-func (g *Group) syncAllJoinedGroups(ctx context.Context) ([]*sdkws.GroupInfo, error) {
- groups, err := g.GetServerJoinGroup(ctx)
- if err != nil {
- return nil, err
- }
- localData, err := g.db.GetJoinedGroupListDB(ctx)
- if err != nil {
- return nil, err
- }
- if err := g.groupSyncer.Sync(ctx, datautil.Batch(ServerGroupToLocalGroup, groups), localData, nil); err != nil {
- return nil, err
- }
- return groups, nil
-}
-
-func (g *Group) SyncAllSelfGroupApplication(ctx context.Context) error {
- list, err := g.GetServerSelfGroupApplication(ctx)
- if err != nil {
- return err
- }
- localData, err := g.db.GetSendGroupApplication(ctx)
- if err != nil {
- return err
- }
- if err := g.groupRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalGroupRequest, list), localData, nil); err != nil {
- return err
- }
- // todo
- return nil
-}
-
-func (g *Group) SyncAllSelfGroupApplicationWithoutNotice(ctx context.Context) error {
- list, err := g.GetServerSelfGroupApplication(ctx)
- if err != nil {
- return err
- }
- localData, err := g.db.GetSendGroupApplication(ctx)
- if err != nil {
- return err
- }
- if err := g.groupRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalGroupRequest, list), localData, nil, false, true); err != nil {
- return err
- }
- // todo
- return nil
-}
-
-func (g *Group) SyncSelfGroupApplications(ctx context.Context, groupIDs ...string) error {
- return g.SyncAllSelfGroupApplication(ctx)
-}
-
-func (g *Group) SyncAllAdminGroupApplication(ctx context.Context) error {
- requests, err := g.GetServerAdminGroupApplicationList(ctx)
- if err != nil {
- return err
- }
- localData, err := g.db.GetAdminGroupApplication(ctx)
- if err != nil {
- return err
- }
- return g.groupAdminRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalAdminGroupRequest, requests), localData, nil)
-}
-
-func (g *Group) SyncAllAdminGroupApplicationWithoutNotice(ctx context.Context) error {
- requests, err := g.GetServerAdminGroupApplicationList(ctx)
- if err != nil {
- return err
- }
- localData, err := g.db.GetAdminGroupApplication(ctx)
- if err != nil {
- return err
- }
- return g.groupAdminRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalAdminGroupRequest, requests), localData, nil, false, true)
-}
-
-func (g *Group) SyncAdminGroupApplications(ctx context.Context, groupIDs ...string) error {
- return g.SyncAllAdminGroupApplication(ctx)
-}
-
-func (g *Group) GetServerJoinGroup(ctx context.Context) ([]*sdkws.GroupInfo, error) {
- fn := func(resp *group.GetJoinedGroupListResp) []*sdkws.GroupInfo { return resp.Groups }
- req := &group.GetJoinedGroupListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
- return util.GetPageAll(ctx, constant.GetJoinedGroupListRouter, req, fn)
-}
-
-func (g *Group) GetServerAdminGroupApplicationList(ctx context.Context) ([]*sdkws.GroupRequest, error) {
- fn := func(resp *group.GetGroupApplicationListResp) []*sdkws.GroupRequest { return resp.GroupRequests }
- req := &group.GetGroupApplicationListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
- return util.GetPageAll(ctx, constant.GetRecvGroupApplicationListRouter, req, fn)
-}
-
-func (g *Group) GetServerSelfGroupApplication(ctx context.Context) ([]*sdkws.GroupRequest, error) {
- fn := func(resp *group.GetGroupApplicationListResp) []*sdkws.GroupRequest { return resp.GroupRequests }
- req := &group.GetUserReqApplicationListReq{UserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
- return util.GetPageAll(ctx, constant.GetSendGroupApplicationListRouter, req, fn)
-}
-
-func (g *Group) GetServerGroupMembers(ctx context.Context, groupID string) ([]*sdkws.GroupMemberFullInfo, error) {
- req := &group.GetGroupMemberListReq{GroupID: groupID, Pagination: &sdkws.RequestPagination{}}
- fn := func(resp *group.GetGroupMemberListResp) []*sdkws.GroupMemberFullInfo { return resp.Members }
- return util.GetPageAll(ctx, constant.GetGroupMemberListRouter, req, fn)
-}
-
-func (g *Group) GetDesignatedGroupMembers(ctx context.Context, groupID string, userID []string) ([]*sdkws.GroupMemberFullInfo, error) {
- resp := &group.GetGroupMembersInfoResp{}
- if err := util.ApiPost(ctx, constant.GetGroupMembersInfoRouter, &group.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userID}, resp); err != nil {
- return nil, err
- }
- return resp.Members, nil
-}
-
-func (g *Group) GetGroupAbstractInfo(ctx context.Context, groupID string) (*group.GroupAbstractInfo, error) {
- resp, err := util.CallApi[group.GetGroupAbstractInfoResp](ctx, constant.GetGroupAbstractInfoRouter, &group.GetGroupAbstractInfoReq{GroupIDs: []string{groupID}})
- if err != nil {
- return nil, err
- }
- if len(resp.GroupAbstractInfos) == 0 {
- return nil, errors.New("group not found")
- }
- return resp.GroupAbstractInfos[0], nil
-}
diff --git a/internal/group/sync2_test.go b/internal/group/sync2_test.go
deleted file mode 100644
index 654521e1a..000000000
--- a/internal/group/sync2_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package group
diff --git a/internal/group/types.go b/internal/group/types.go
deleted file mode 100644
index 2d7641a70..000000000
--- a/internal/group/types.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package group
-
-import "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-
-type GetGroupMemberListV2Response struct {
- GroupMembersList []*model_struct.LocalGroupMember
- IsEnd bool `json:"isEnd"`
-}
-
-type GetGroupListV2Response struct {
- GroupsList []*model_struct.LocalGroup
- IsEnd bool `json:"isEnd"`
-}
diff --git a/internal/interaction/compressor.go b/internal/interaction/compressor.go
index a03adfe51..a1265a2d8 100644
--- a/internal/interaction/compressor.go
+++ b/internal/interaction/compressor.go
@@ -18,7 +18,7 @@ import (
"bytes"
"compress/gzip"
"errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/tools/errs"
"io"
"sync"
)
@@ -47,10 +47,10 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
gzipBuffer := bytes.Buffer{}
gz := gzip.NewWriter(&gzipBuffer)
if _, err := gz.Write(rawData); err != nil {
- return nil, utils.Wrap(err, "")
+ return nil, errs.WrapMsg(err, "")
}
if err := gz.Close(); err != nil {
- return nil, utils.Wrap(err, "")
+ return nil, errs.WrapMsg(err, "")
}
return gzipBuffer.Bytes(), nil
}
@@ -63,10 +63,10 @@ func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) {
gz.Reset(&gzipBuffer)
if _, err := gz.Write(rawData); err != nil {
- return nil, utils.Wrap(err, "")
+ return nil, errs.WrapMsg(err, "")
}
if err := gz.Close(); err != nil {
- return nil, utils.Wrap(err, "")
+ return nil, errs.WrapMsg(err, "")
}
return gzipBuffer.Bytes(), nil
}
@@ -75,11 +75,11 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
buff := bytes.NewBuffer(compressedData)
reader, err := gzip.NewReader(buff)
if err != nil {
- return nil, utils.Wrap(err, "NewReader failed")
+ return nil, errs.WrapMsg(err, "NewReader failed")
}
compressedData, err = io.ReadAll(reader)
if err != nil {
- return nil, utils.Wrap(err, "ReadAll failed")
+ return nil, errs.WrapMsg(err, "ReadAll failed")
}
_ = reader.Close()
return compressedData, nil
@@ -94,12 +94,12 @@ func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, erro
err := reader.Reset(bytes.NewReader(compressedData))
if err != nil {
- return nil, utils.Wrap(err, "NewReader failed")
+ return nil, errs.WrapMsg(err, "NewReader failed")
}
compressedData, err = io.ReadAll(reader)
if err != nil {
- return nil, utils.Wrap(err, "ReadAll failed")
+ return nil, errs.WrapMsg(err, "ReadAll failed")
}
_ = reader.Close()
return compressedData, nil
diff --git a/internal/interaction/encoder.go b/internal/interaction/encoder.go
index 2052b8012..6ed8e0080 100644
--- a/internal/interaction/encoder.go
+++ b/internal/interaction/encoder.go
@@ -17,7 +17,7 @@ package interaction
import (
"bytes"
"encoding/gob"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/tools/errs"
)
type Encoder interface {
@@ -45,7 +45,7 @@ func (g *GobEncoder) Decode(encodeData []byte, decodeData interface{}) error {
dec := gob.NewDecoder(buff)
err := dec.Decode(decodeData)
if err != nil {
- return utils.Wrap(err, "")
+ return errs.Wrap(err)
}
return nil
}
diff --git a/internal/interaction/long_conn_mgr.go b/internal/interaction/long_conn_mgr.go
index d530c8072..252f6ded2 100644
--- a/internal/interaction/long_conn_mgr.go
+++ b/internal/interaction/long_conn_mgr.go
@@ -21,21 +21,21 @@ import (
"fmt"
"io"
"runtime"
+ "runtime/debug"
"strconv"
"strings"
"sync"
"time"
+ "github.com/golang/protobuf/proto"
+ "github.com/gorilla/websocket"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
-
- "github.com/golang/protobuf/proto"
- "github.com/gorilla/websocket"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs"
@@ -50,13 +50,15 @@ const (
pongWait = 30 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
- pingPeriod = (pongWait * 9) / 10
+ pingPeriod = (pongWait * 8) / 10
// Maximum message size allowed from peer.
maxMessageSize = 1024 * 1024
//Maximum number of reconnection attempts
maxReconnectAttempts = 300
+
+ sendAndWaitTime = time.Second * 10
)
const (
@@ -66,11 +68,6 @@ const (
Connected
)
-var (
- newline = []byte{'\n'}
- space = []byte{' '}
-)
-
var (
ErrChanClosed = errors.New("send channel closed")
ErrConnClosed = errors.New("conn has closed")
@@ -84,14 +81,14 @@ type LongConnMgr struct {
w sync.Mutex
connStatus int
// The long connection,can be set tcp or websocket.
- conn LongConn
- listener open_im_sdk_callback.OnConnListener
+ conn LongConn
+ listener open_im_sdk_callback.OnConnListener
+ userOnline func(map[string][]int32)
// Buffered channel of outbound messages.
send chan Message
pushMsgAndMaxSeqCh chan common.Cmd2Value
conversationCh chan common.Cmd2Value
loginMgrCh chan common.Cmd2Value
- heartbeatCh chan common.Cmd2Value
closedErr error
ctx context.Context
IsCompression bool
@@ -104,6 +101,8 @@ type LongConnMgr struct {
IsBackground bool
// write conn lock
connWrite *sync.Mutex
+
+ sub *subscription
}
type Message struct {
@@ -111,16 +110,23 @@ type Message struct {
Resp chan *GeneralWsResp
}
-func NewLongConnMgr(ctx context.Context, listener open_im_sdk_callback.OnConnListener, heartbeatCmdCh, pushMsgAndMaxSeqCh, loginMgrCh chan common.Cmd2Value) *LongConnMgr {
- l := &LongConnMgr{listener: listener, pushMsgAndMaxSeqCh: pushMsgAndMaxSeqCh,
- loginMgrCh: loginMgrCh, IsCompression: true,
- Syncer: NewWsRespAsyn(), encoder: NewGobEncoder(), compressor: NewGzipCompressor(),
- reconnectStrategy: NewExponentialRetry()}
+func NewLongConnMgr(ctx context.Context, listener open_im_sdk_callback.OnConnListener, userOnline func(map[string][]int32), pushMsgAndMaxSeqCh, loginMgrCh chan common.Cmd2Value) *LongConnMgr {
+ l := &LongConnMgr{
+ listener: listener,
+ userOnline: userOnline,
+ pushMsgAndMaxSeqCh: pushMsgAndMaxSeqCh,
+ loginMgrCh: loginMgrCh,
+ IsCompression: true,
+ Syncer: NewWsRespAsyn(),
+ encoder: NewGobEncoder(),
+ compressor: NewGzipCompressor(),
+ reconnectStrategy: NewExponentialRetry(),
+ sub: newSubscription(),
+ }
l.send = make(chan Message, 10)
l.conn = NewWebSocket(WebSocket)
l.connWrite = new(sync.Mutex)
l.ctx = ctx
- l.heartbeatCh = heartbeatCmdCh
return l
}
func (c *LongConnMgr) Run(ctx context.Context) {
@@ -169,6 +175,14 @@ func (c *LongConnMgr) SendReqWaitResp(ctx context.Context, m proto.Message, reqI
// reads from this goroutine.
func (c *LongConnMgr) readPump(ctx context.Context) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+
+ log.ZWarn(ctx, "readPump panic", nil, "panic info", err)
+ }
+ }()
+
log.ZDebug(ctx, "readPump start", "goroutine ID:", getGoroutineID())
defer func() {
_ = c.close()
@@ -176,6 +190,13 @@ func (c *LongConnMgr) readPump(ctx context.Context) {
}()
connNum := 0
for {
+ select {
+ case <-ctx.Done():
+ c.closedErr = ctx.Err()
+ log.ZInfo(c.ctx, "readPump done, sdk logout.....")
+ return
+ default:
+ }
ctx = ccontext.WithOperationID(ctx, utils.OperationIDGenerator())
needRecon, err := c.reConn(ctx, &connNum)
if !needRecon {
@@ -193,6 +214,7 @@ func (c *LongConnMgr) readPump(ctx context.Context) {
if err != nil {
log.ZError(c.ctx, "readMessage err", err, "goroutine ID:", getGoroutineID())
_ = c.close()
+ c.sub.onConnClosed(err)
continue
}
switch messageType {
@@ -210,7 +232,6 @@ func (c *LongConnMgr) readPump(ctx context.Context) {
return
default:
}
-
}
}
@@ -220,6 +241,14 @@ func (c *LongConnMgr) readPump(ctx context.Context) {
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *LongConnMgr) writePump(ctx context.Context) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+
+ log.ZWarn(ctx, "writePump panic", nil, "panic info", err)
+ }
+ }()
+
log.ZDebug(ctx, "writePump start", "goroutine ID:", getGoroutineID())
defer func() {
@@ -230,6 +259,7 @@ func (c *LongConnMgr) writePump(ctx context.Context) {
select {
case <-ctx.Done():
c.closedErr = ctx.Err()
+ log.ZInfo(c.ctx, "writePump done, sdk logout.....")
return
case message, ok := <-c.send:
if !ok {
@@ -268,6 +298,14 @@ func (c *LongConnMgr) writePump(ctx context.Context) {
}
func (c *LongConnMgr) heartbeat(ctx context.Context) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+
+ log.ZWarn(ctx, "heartbeat panic", nil, "panic info", err)
+ }
+ }()
+
log.ZDebug(ctx, "heartbeat start", "goroutine ID:", getGoroutineID())
ticker := time.NewTicker(pingPeriod)
defer func() {
@@ -279,81 +317,42 @@ func (c *LongConnMgr) heartbeat(ctx context.Context) {
case <-ctx.Done():
log.ZInfo(ctx, "heartbeat done sdk logout.....")
return
- case <-c.heartbeatCh:
- c.retrieveMaxSeq(ctx)
case <-ticker.C:
+ log.ZInfo(ctx, "sendPingMessage", "goroutine ID:", getGoroutineID())
c.sendPingMessage(ctx)
}
}
}
+
func (c *LongConnMgr) sendPingMessage(ctx context.Context) {
c.connWrite.Lock()
defer c.connWrite.Unlock()
- log.ZInfo(ctx, "ping message tart", "goroutine ID:", getGoroutineID())
+ opid := utils.OperationIDGenerator()
+ log.ZDebug(ctx, "ping Message Started", "goroutine ID:", getGoroutineID(), "opid", opid)
if c.IsConnected() {
+ log.ZDebug(ctx, "ping Message Started isConnected", "goroutine ID:", getGoroutineID(), "opid", opid)
c.conn.SetWriteDeadline(writeWait)
- if err := c.conn.WriteMessage(PingMessage, nil); err != nil {
+ if err := c.conn.WriteMessage(PingMessage, []byte(opid)); err != nil {
+ log.ZWarn(ctx, "ping Message failed", err, "goroutine ID:", getGoroutineID(), "opid", opid)
return
}
+ } else {
+ log.ZDebug(ctx, "ping Message failed, connection", "connStatus", c.GetConnectionStatus(), "goroutine ID:", getGoroutineID(), "opid", opid)
}
-
}
+
func getGoroutineID() int64 {
buf := make([]byte, 64)
buf = buf[:runtime.Stack(buf, false)]
idField := strings.Fields(strings.TrimPrefix(string(buf), "goroutine "))[0]
id, err := strconv.ParseInt(idField, 10, 64)
if err != nil {
- panic(fmt.Sprintf("cannot get goroutine id: %v", err))
+ return 0
}
return id
}
-func (c *LongConnMgr) retrieveMaxSeq(ctx context.Context) {
- if c.conn == nil {
- return
- }
- var m sdkws.GetMaxSeqReq
- m.UserID = ccontext.Info(ctx).UserID()
- opID := utils.OperationIDGenerator()
- sCtx := ccontext.WithOperationID(c.ctx, opID)
- log.ZInfo(sCtx, "retrieveMaxSeq start", "goroutine ID:", getGoroutineID())
- data, err := proto.Marshal(&m)
- if err != nil {
- log.ZError(sCtx, "proto.Marshal", err)
- return
- }
- req := &GeneralWsReq{
- ReqIdentifier: constant.GetNewestSeq,
- SendID: m.UserID,
- OperationID: opID,
- Data: data,
- }
- resp, err := c.sendAndWaitResp(req)
- if err != nil {
- log.ZError(sCtx, "sendAndWaitResp", err)
- _ = c.close()
- time.Sleep(time.Second * 1)
- return
- } else {
- if resp.ErrCode != 0 {
- log.ZError(sCtx, "retrieveMaxSeq failed", nil, "errCode:", resp.ErrCode, "errMsg:", resp.ErrMsg)
- }
- var wsSeqResp sdkws.GetMaxSeqResp
- err = proto.Unmarshal(resp.Data, &wsSeqResp)
- if err != nil {
- log.ZError(sCtx, "proto.Unmarshal", err)
- }
- var cmd sdk_struct.CmdMaxSeqToMsgSync
- cmd.ConversationMaxSeqOnSvr = wsSeqResp.MaxSeqs
-
- err := common.TriggerCmdMaxSeq(sCtx, &cmd, c.pushMsgAndMaxSeqCh)
- if err != nil {
- log.ZError(sCtx, "TriggerCmdMaxSeq failed", err)
- }
- }
-}
func (c *LongConnMgr) sendAndWaitResp(msg *GeneralWsReq) (*GeneralWsResp, error) {
tempChan, err := c.writeBinaryMsgAndRetry(msg)
defer c.Syncer.DelCh(msg.MsgIncr)
@@ -363,7 +362,7 @@ func (c *LongConnMgr) sendAndWaitResp(msg *GeneralWsReq) (*GeneralWsResp, error)
select {
case resp := <-tempChan:
return resp, nil
- case <-time.After(time.Second * 5):
+ case <-time.After(sendAndWaitTime):
return nil, sdkerrs.ErrNetworkTimeOut
}
@@ -391,9 +390,50 @@ func (c *LongConnMgr) writeBinaryMsgAndRetry(msg *GeneralWsReq) (chan *GeneralWs
return nil, sdkerrs.ErrNetwork.WrapMsg("send binary message error")
}
+func (c *LongConnMgr) writeBinaryMsgAndNotRetry(msg *GeneralWsReq) (chan *GeneralWsResp, error) {
+ msgIncr, tempChan := c.Syncer.AddCh(msg.SendID)
+ msg.MsgIncr = msgIncr
+ if err := c.writeBinaryMsg(*msg); err != nil {
+ c.Syncer.DelCh(msgIncr)
+ return nil, err
+ }
+ return tempChan, nil
+}
+
func (c *LongConnMgr) writeBinaryMsg(req GeneralWsReq) error {
c.connWrite.Lock()
defer c.connWrite.Unlock()
+ return c.writeBinaryMsgNoLock(req)
+}
+
+func (c *LongConnMgr) writeSubInfo(subscribeUserID, unsubscribeUserID []string, lock bool) error {
+ opID := utils.OperationIDGenerator()
+ sCtx := ccontext.WithOperationID(c.ctx, opID)
+ log.ZInfo(sCtx, "writeSubInfo start", "goroutine ID:", getGoroutineID())
+ subReq := sdkws.SubUserOnlineStatus{
+ SubscribeUserID: subscribeUserID,
+ UnsubscribeUserID: unsubscribeUserID,
+ }
+ data, err := proto.Marshal(&subReq)
+ if err != nil {
+ log.ZError(sCtx, "proto.Marshal", err)
+ return err
+ }
+ req := GeneralWsReq{
+ ReqIdentifier: constant.WsSubUserOnlineStatus,
+ SendID: ccontext.Info(sCtx).UserID(),
+ OperationID: opID,
+ MsgIncr: utils.OperationIDGenerator(),
+ Data: data,
+ }
+ if lock {
+ return c.writeBinaryMsg(req)
+ } else {
+ return c.writeBinaryMsgNoLock(req)
+ }
+}
+
+func (c *LongConnMgr) writeBinaryMsgNoLock(req GeneralWsReq) error {
encodeBuf, err := c.encoder.Encode(req)
if err != nil {
return err
@@ -412,6 +452,7 @@ func (c *LongConnMgr) writeBinaryMsg(req GeneralWsReq) error {
return c.conn.WriteMessage(MessageBinary, encodeBuf)
}
}
+
func (c *LongConnMgr) close() error {
c.w.Lock()
defer c.w.Unlock()
@@ -452,14 +493,21 @@ func (c *LongConnMgr) handleMessage(message []byte) error {
}
return sdkerrs.ErrLoginOut
case constant.KickOnlineMsg:
- log.ZDebug(ctx, "client kicked offline")
- c.listener.OnKickedOffline()
- _ = common.TriggerCmdLogOut(ctx, c.loginMgrCh)
- return errors.New("client kicked offline")
+ log.ZDebug(ctx, "socket receive client kicked offline")
+
+ err = errs.ErrTokenKicked.WrapMsg("socket receive client kicked offline")
+ ccontext.GetApiErrCodeCallback(ctx).OnError(ctx, err)
+ return err
case constant.GetNewestSeq:
fallthrough
+ case constant.PullMsgByRange:
+ fallthrough
case constant.PullMsgBySeqList:
fallthrough
+ case constant.GetConvMaxReadSeq:
+ fallthrough
+ case constant.PullConvLastMessage:
+ fallthrough
case constant.SendMsg:
fallthrough
case constant.SendSignalMsg:
@@ -469,12 +517,94 @@ func (c *LongConnMgr) handleMessage(message []byte) error {
log.ZError(ctx, "notifyResp failed", err, "reqIdentifier", wsResp.ReqIdentifier, "errCode",
wsResp.ErrCode, "errMsg", wsResp.ErrMsg, "msgIncr", wsResp.MsgIncr, "operationID", wsResp.OperationID)
}
+ case constant.WsSubUserOnlineStatus:
+ if err := c.handlerUserOnlineChange(ctx, wsResp); err != nil {
+ log.ZError(ctx, "handlerUserOnlineChange failed", err, "wsResp", wsResp)
+ }
default:
- // log.Error(wsResp.OperationID, "type failed, ", wsResp.ReqIdentifier)
return sdkerrs.ErrMsgBinaryTypeNotSupport
}
return nil
}
+
+func (c *LongConnMgr) handlerUserOnlineChange(ctx context.Context, wsResp GeneralWsResp) error {
+ if wsResp.ErrCode != 0 {
+ return errs.New("handlerUserOnlineChange failed")
+ }
+ var tips sdkws.SubUserOnlineStatusTips
+ if err := proto.Unmarshal(wsResp.Data, &tips); err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "handlerUserOnlineChange", "tips", &tips)
+ c.callbackUserOnlineChange(c.sub.setUserState(tips.Subscribers))
+ return nil
+}
+
+func (c *LongConnMgr) GetUserOnlinePlatformIDs(ctx context.Context, userIDs []string) (map[string][]int32, error) {
+ ctx, cancel := context.WithTimeout(ctx, time.Second*5)
+ defer cancel()
+ exist, wait, subUserIDs, unsubUserIDs := c.sub.getUserOnline(userIDs)
+ if len(subUserIDs)+len(unsubUserIDs) > 0 {
+ if err := c.writeSubInfo(subUserIDs, unsubUserIDs, true); err != nil {
+ c.sub.writeFailed(wait, err)
+ return nil, err
+ }
+ }
+ for userID, statues := range wait {
+ select {
+ case <-ctx.Done():
+ return nil, context.Cause(ctx)
+ case <-statues.Done():
+ online, err := statues.Result()
+ if err != nil {
+ return nil, err
+ }
+ exist[userID] = online
+ }
+ }
+ return exist, nil
+}
+
+func (c *LongConnMgr) UnsubscribeUserOnlinePlatformIDs(ctx context.Context, userIDs []string) error {
+ if len(userIDs) > 0 {
+ c.sub.unsubscribe(userIDs)
+ }
+ return nil
+}
+
+func (c *LongConnMgr) writeConnFirstSubMsg(ctx context.Context) error {
+ userIDs := c.sub.getNewConnSubUserIDs()
+ log.ZDebug(ctx, "writeConnFirstSubMsg getNewConnSubUserIDs", "userIDs", userIDs)
+ if len(userIDs) == 0 {
+ return nil
+ }
+ if err := c.writeSubInfo(userIDs, nil, false); err != nil {
+ c.sub.onConnClosed(err)
+ return err
+ }
+ return nil
+}
+
+func (c *LongConnMgr) callbackUserOnlineChange(users map[string][]int32) {
+ log.ZDebug(c.ctx, "#### ===> callbackUserOnlineChange", "users", users)
+ if len(users) == 0 {
+ return
+ }
+ c.userOnline(users)
+ //for userID, onlinePlatformIDs := range users {
+ // status := userPb.OnlineStatus{
+ // UserID: userID,
+ // PlatformIDs: onlinePlatformIDs,
+ // }
+ // if len(status.PlatformIDs) == 0 {
+ // status.Status = constant.Offline
+ // } else {
+ // status.Status = constant.Online
+ // }
+ // c.userOnline.OnUserStatusChanged(utils.StructToJsonString(users))
+ //}
+}
+
func (c *LongConnMgr) IsConnected() bool {
c.w.Lock()
defer c.w.Unlock()
@@ -495,6 +625,7 @@ func (c *LongConnMgr) SetConnectionStatus(status int) {
defer c.w.Unlock()
c.connStatus = status
}
+
func (c *LongConnMgr) reConn(ctx context.Context, num *int) (needRecon bool, err error) {
if c.IsConnected() {
return true, nil
@@ -546,11 +677,20 @@ func (c *LongConnMgr) reConn(ctx context.Context, num *int) (needRecon bool, err
c.listener.OnConnectFailed(sdkerrs.NetworkError, err.Error())
return true, err
}
+ if err := c.writeConnFirstSubMsg(ctx); err != nil {
+ log.ZError(ctx, "first write user online sub info error", err)
+ ccontext.GetApiErrCodeCallback(ctx).OnError(ctx, err)
+ c.listener.OnConnectFailed(sdkerrs.NetworkError, err.Error())
+ c.conn.Close()
+ return true, err
+ }
c.listener.OnConnectSuccess()
+ c.sub.onConnSuccess()
c.ctx = newContext(c.conn.LocalAddr())
c.ctx = context.WithValue(ctx, "ConnContext", c.ctx)
c.SetConnectionStatus(Connected)
c.conn.SetPongHandler(c.pongHandler)
+ c.conn.SetPingHandler(c.pingHandler)
*num++
log.ZInfo(c.ctx, "long conn establish success", "localAddr", c.conn.LocalAddr(), "connNum", *num)
c.reconnectStrategy.Reset()
@@ -587,6 +727,7 @@ func (c *LongConnMgr) SetBackground(isBackground bool) {
c.IsBackground = isBackground
}
+// receive ping and send pong.
func (c *LongConnMgr) pingHandler(_ string) error {
if err := c.conn.SetReadDeadline(pongWait); err != nil {
return err
@@ -595,7 +736,9 @@ func (c *LongConnMgr) pingHandler(_ string) error {
return c.writePongMsg()
}
-func (c *LongConnMgr) pongHandler(_ string) error {
+// when client send pong.
+func (c *LongConnMgr) pongHandler(appData string) error {
+ log.ZDebug(c.ctx, "server Pong Message Received", "appData", appData)
if err := c.conn.SetReadDeadline(pongWait); err != nil {
return err
}
diff --git a/internal/interaction/msg_sync.go b/internal/interaction/msg_sync.go
index 867594dd4..6db367d14 100644
--- a/internal/interaction/msg_sync.go
+++ b/internal/interaction/msg_sync.go
@@ -16,14 +16,22 @@ package interaction
import (
"context"
+ "errors"
+ "fmt"
+ "runtime/debug"
"strings"
"sync"
+ "time"
+
+ "golang.org/x/sync/errgroup"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
@@ -33,34 +41,40 @@ const (
connectPullNums = 1
defaultPullNums = 10
SplitPullMsgNum = 100
+
+ pullMsgGoroutineLimit = 10
)
-// The callback synchronization starts. The reconnection ends
+// MsgSyncer is a central hub for message relay, responsible for sequential message gap pulling,
+// handling network events, and managing app foreground and background events.
type MsgSyncer struct {
- loginUserID string // login user ID
- longConnMgr *LongConnMgr // long connection manager
- PushMsgAndMaxSeqCh chan common.Cmd2Value // channel for receiving push messages and the maximum SEQ number
- conversationCh chan common.Cmd2Value // storage and session triggering
- syncedMaxSeqs map[string]int64 // map of the maximum synced SEQ numbers for all group IDs
- db db_interface.DataBase // data store
- syncTimes int // times of sync
- ctx context.Context // context
- reinstalled bool //true if the app was uninstalled and reinstalled
+ loginUserID string // login user ID
+ longConnMgr *LongConnMgr // long connection manager
+ recvCh chan common.Cmd2Value // channel for receiving push messages and the maximum SEQ number
+ conversationCh chan common.Cmd2Value // storage and session triggering
+ syncedMaxSeqs map[string]int64 // map of the maximum synced SEQ numbers for all group IDs
+ syncedMaxSeqsLock sync.RWMutex // syncedMaxSeqs map lock
+ db db_interface.DataBase // data store
+ syncTimes int // times of sync
+ ctx context.Context // context
+ reinstalled bool //true if the app was uninstalled and reinstalled
+ isSyncing bool // indicates whether data is being synced
+ isSyncingLock sync.Mutex // lock for syncing state
}
// NewMsgSyncer creates a new instance of the message synchronizer.
-func NewMsgSyncer(ctx context.Context, conversationCh, PushMsgAndMaxSeqCh chan common.Cmd2Value,
+func NewMsgSyncer(ctx context.Context, conversationCh, recvCh chan common.Cmd2Value,
loginUserID string, longConnMgr *LongConnMgr, db db_interface.DataBase, syncTimes int) (*MsgSyncer, error) {
m := &MsgSyncer{
- loginUserID: loginUserID,
- longConnMgr: longConnMgr,
- PushMsgAndMaxSeqCh: PushMsgAndMaxSeqCh,
- conversationCh: conversationCh,
- ctx: ctx,
- syncedMaxSeqs: make(map[string]int64),
- db: db,
- syncTimes: syncTimes,
+ loginUserID: loginUserID,
+ longConnMgr: longConnMgr,
+ recvCh: recvCh,
+ conversationCh: conversationCh,
+ ctx: ctx,
+ syncedMaxSeqs: make(map[string]int64),
+ db: db,
+ syncTimes: syncTimes,
}
if err := m.loadSeq(ctx); err != nil {
log.ZError(ctx, "loadSeq err", err)
@@ -76,27 +90,38 @@ func (m *MsgSyncer) loadSeq(ctx context.Context) error {
log.ZError(ctx, "get conversation id list failed", err)
return err
}
+
if len(conversationIDList) == 0 {
- m.reinstalled = true
+ version, err := m.db.GetAppSDKVersion(ctx)
+ if err != nil && !errors.Is(err, errs.ErrRecordNotFound) {
+ return err
+ }
+ if version == nil || !version.Installed {
+ m.reinstalled = true
+ }
}
- // TODO With a large number of sessions, this could potentially cause blocking and needs optimization.
+ // TODO With a large number of sessions(10w), this could potentially cause blocking and needs optimization.
+
type SyncedSeq struct {
ConversationID string
MaxSyncedSeq int64
Err error
}
- concurrency := 20
- partSize := len(conversationIDList) / concurrency
+ partSize := 20
+ currency := (len(conversationIDList)-1)/partSize + 1
+ if len(conversationIDList) == 0 {
+ currency = 0
+ }
var wg sync.WaitGroup
- resultMaps := make([]map[string]SyncedSeq, concurrency)
+ resultMaps := make([]map[string]SyncedSeq, currency)
- for i := 0; i < concurrency; i++ {
+ for i := 0; i < currency; i++ {
wg.Add(1)
start := i * partSize
end := start + partSize
- if i == concurrency-1 {
+ if i == currency-1 {
end = len(conversationIDList)
}
@@ -105,7 +130,7 @@ func (m *MsgSyncer) loadSeq(ctx context.Context) error {
go func(i, start, end int) {
defer wg.Done()
for _, v := range conversationIDList[start:end] {
- maxSyncedSeq, err := m.db.GetConversationNormalMsgSeqNoInit(ctx, v)
+ maxSyncedSeq, err := m.db.CheckConversationNormalMsgSeq(ctx, v)
resultMaps[i][v] = SyncedSeq{
ConversationID: v,
MaxSyncedSeq: maxSyncedSeq,
@@ -121,7 +146,7 @@ func (m *MsgSyncer) loadSeq(ctx context.Context) error {
for _, resultMap := range resultMaps {
for k, v := range resultMap {
if v.Err != nil {
- log.ZError(ctx, "get group normal seq failed", v.Err, "conversationID", k)
+ log.ZError(ctx, "get group normal seq failed", errs.Wrap(v.Err), "conversationID", k)
continue
}
m.syncedMaxSeqs[k] = v.MaxSyncedSeq
@@ -142,9 +167,16 @@ func (m *MsgSyncer) loadSeq(ctx context.Context) error {
// DoListener Listen to the message pipe of the message synchronizer
// and process received and pushed messages
func (m *MsgSyncer) DoListener(ctx context.Context) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+
+ log.ZWarn(ctx, "DoListener panic", nil, "panic info", err)
+ }
+ }()
for {
select {
- case cmd := <-m.PushMsgAndMaxSeqCh:
+ case cmd := <-m.recvCh:
m.handlePushMsgAndEvent(cmd)
case <-ctx.Done():
log.ZInfo(m.ctx, "msg syncer done, sdk logout.....")
@@ -167,10 +199,23 @@ func (m *MsgSyncer) handlePushMsgAndEvent(cmd common.Cmd2Value) {
switch cmd.Cmd {
case constant.CmdConnSuccesss:
log.ZInfo(cmd.Ctx, "recv long conn mgr connected", "cmd", cmd.Cmd, "value", cmd.Value)
- m.doConnected(cmd.Ctx)
- case constant.CmdMaxSeq:
- log.ZInfo(cmd.Ctx, "recv max seqs from long conn mgr, start sync msgs", "cmd", cmd.Cmd, "value", cmd.Value)
- m.compareSeqsAndBatchSync(cmd.Ctx, cmd.Value.(*sdk_struct.CmdMaxSeqToMsgSync).ConversationMaxSeqOnSvr, defaultPullNums)
+ if m.startSync() {
+ m.doConnected(cmd.Ctx)
+ } else {
+ log.ZWarn(cmd.Ctx, "syncing, ignore connected event", nil, "cmd", cmd.Cmd, "value", cmd.Value)
+ }
+ case constant.CmdWakeUpDataSync:
+ log.ZInfo(cmd.Ctx, "app wake up, start sync msgs", "cmd", cmd.Cmd, "value", cmd.Value)
+ if m.startSync() {
+ m.doWakeupDataSync(cmd.Ctx)
+ } else {
+ log.ZWarn(cmd.Ctx, "syncing, ignore wake up event", nil, "cmd", cmd.Cmd, "value", cmd.Value)
+
+ }
+ case constant.CmdIMMessageSync:
+ log.ZInfo(cmd.Ctx, "manually trigger IM message synchronization", "cmd", cmd.Cmd, "value", cmd.Value)
+ m.doIMMessageSync(cmd.Ctx)
+
case constant.CmdPushMsg:
m.doPushMsg(cmd.Ctx, cmd.Value.(*sdkws.PushMessages))
}
@@ -184,7 +229,9 @@ func (m *MsgSyncer) compareSeqsAndBatchSync(ctx context.Context, maxSeqToSync ma
messagesSeqMap := make(map[string]int64)
for conversationID, seq := range maxSeqToSync {
if IsNotification(conversationID) {
- notificationsSeqMap[conversationID] = seq
+ if seq != 0 { // seq is 0, no need to sync
+ notificationsSeqMap[conversationID] = seq
+ }
} else {
messagesSeqMap[conversationID] = seq
}
@@ -200,10 +247,13 @@ func (m *MsgSyncer) compareSeqsAndBatchSync(ctx context.Context, maxSeqToSync ma
m.syncedMaxSeqs[conversationID] = seq
}
- err := m.db.BatchInsertNotificationSeq(ctx, notificationSeqs)
- if err != nil {
- log.ZWarn(ctx, "BatchInsertNotificationSeq err", err)
+ if len(notificationSeqs) > 0 {
+ err := m.db.BatchInsertNotificationSeq(ctx, notificationSeqs)
+ if err != nil {
+ log.ZWarn(ctx, "BatchInsertNotificationSeq err", err)
+ }
}
+
for conversationID, maxSeq := range messagesSeqMap {
if syncedMaxSeq, ok := m.syncedMaxSeqs[conversationID]; ok {
if maxSeq > syncedMaxSeq {
@@ -213,7 +263,15 @@ func (m *MsgSyncer) compareSeqsAndBatchSync(ctx context.Context, maxSeqToSync ma
needSyncSeqMap[conversationID] = [2]int64{0, maxSeq}
}
}
- m.reinstalled = false
+ defer func() {
+ if err := m.db.SetAppSDKVersion(ctx, &model_struct.LocalAppSDKVersion{
+ Installed: true,
+ }); err != nil {
+ log.ZError(ctx, "SetAppSDKVersion err", err)
+ }
+ m.reinstalled = false
+ }()
+ _ = m.syncAndTriggerReinstallMsgs(m.ctx, needSyncSeqMap, pullNums)
} else {
for conversationID, maxSeq := range maxSeqToSync {
if syncedMaxSeq, ok := m.syncedMaxSeqs[conversationID]; ok {
@@ -221,11 +279,38 @@ func (m *MsgSyncer) compareSeqsAndBatchSync(ctx context.Context, maxSeqToSync ma
needSyncSeqMap[conversationID] = [2]int64{syncedMaxSeq + 1, maxSeq}
}
} else {
- needSyncSeqMap[conversationID] = [2]int64{0, maxSeq}
+ if maxSeq != 0 { // seq is 0, no need to sync
+ needSyncSeqMap[conversationID] = [2]int64{0, maxSeq}
+ }
}
}
+ _ = m.syncAndTriggerMsgs(m.ctx, needSyncSeqMap, pullNums)
+ }
+}
+
+// startSync checks if the sync is already in progress.
+// If syncing is in progress, it returns false. Otherwise, it starts syncing and returns true.
+func (m *MsgSyncer) startSync() bool {
+ m.isSyncingLock.Lock()
+ defer m.isSyncingLock.Unlock()
+
+ if m.isSyncing {
+ // If already syncing, return false
+ return false
}
- _ = m.syncAndTriggerMsgs(m.ctx, needSyncSeqMap, pullNums)
+
+ // Set syncing to true and start the sync
+ m.isSyncing = true
+
+ // Create a goroutine that waits for 5 seconds and then sets isSyncing to false
+ go func() {
+ time.Sleep(5 * time.Second)
+ m.isSyncingLock.Lock()
+ m.isSyncing = false
+ m.isSyncingLock.Unlock()
+ }()
+
+ return true
}
func (m *MsgSyncer) doPushMsg(ctx context.Context, push *sdkws.PushMessages) {
@@ -234,14 +319,14 @@ func (m *MsgSyncer) doPushMsg(ctx context.Context, push *sdkws.PushMessages) {
m.pushTriggerAndSync(ctx, push.NotificationMsgs, m.triggerNotification)
}
-func (m *MsgSyncer) pushTriggerAndSync(ctx context.Context, pullMsgs map[string]*sdkws.PullMsgs, triggerFunc func(ctx context.Context, msgs map[string]*sdkws.PullMsgs) error) {
- if len(pullMsgs) == 0 {
+func (m *MsgSyncer) pushTriggerAndSync(ctx context.Context, pushMessages map[string]*sdkws.PullMsgs, triggerFunc func(ctx context.Context, msgs map[string]*sdkws.PullMsgs) error) {
+ if len(pushMessages) == 0 {
return
}
needSyncSeqMap := make(map[string][2]int64)
var lastSeq int64
var storageMsgs []*sdkws.MsgData
- for conversationID, msgs := range pullMsgs {
+ for conversationID, msgs := range pushMessages {
for _, msg := range msgs.Msgs {
if msg.Seq == 0 {
_ = triggerFunc(ctx, map[string]*sdkws.PullMsgs{conversationID: {Msgs: []*sdkws.MsgData{msg}}})
@@ -266,103 +351,212 @@ func (m *MsgSyncer) pushTriggerAndSync(ctx context.Context, pullMsgs map[string]
func (m *MsgSyncer) doConnected(ctx context.Context) {
reinstalled := m.reinstalled
if reinstalled {
- common.TriggerCmdNotification(m.ctx, sdk_struct.CmdNewMsgComeToConversation{SyncFlag: constant.AppDataSyncStart}, m.conversationCh)
+ common.TriggerCmdSyncFlag(m.ctx, constant.AppDataSyncStart, m.conversationCh)
} else {
- common.TriggerCmdNotification(m.ctx, sdk_struct.CmdNewMsgComeToConversation{SyncFlag: constant.MsgSyncBegin}, m.conversationCh)
+ common.TriggerCmdSyncFlag(m.ctx, constant.MsgSyncBegin, m.conversationCh)
}
var resp sdkws.GetMaxSeqResp
if err := m.longConnMgr.SendReqWaitResp(m.ctx, &sdkws.GetMaxSeqReq{UserID: m.loginUserID}, constant.GetNewestSeq, &resp); err != nil {
log.ZError(m.ctx, "get max seq error", err)
- common.TriggerCmdNotification(m.ctx, sdk_struct.CmdNewMsgComeToConversation{SyncFlag: constant.MsgSyncFailed}, m.conversationCh)
+ common.TriggerCmdSyncFlag(m.ctx, constant.MsgSyncFailed, m.conversationCh)
return
} else {
log.ZDebug(m.ctx, "get max seq success", "resp", resp.MaxSeqs)
}
m.compareSeqsAndBatchSync(ctx, resp.MaxSeqs, connectPullNums)
if reinstalled {
- common.TriggerCmdNotification(m.ctx, sdk_struct.CmdNewMsgComeToConversation{SyncFlag: constant.AppDataSyncFinish}, m.conversationCh)
+ common.TriggerCmdSyncFlag(m.ctx, constant.AppDataSyncFinish, m.conversationCh)
+ } else {
+ common.TriggerCmdSyncFlag(m.ctx, constant.MsgSyncEnd, m.conversationCh)
+ }
+}
+
+func (m *MsgSyncer) doWakeupDataSync(ctx context.Context) {
+ common.TriggerCmdSyncData(ctx, m.conversationCh)
+ var resp sdkws.GetMaxSeqResp
+ if err := m.longConnMgr.SendReqWaitResp(m.ctx, &sdkws.GetMaxSeqReq{UserID: m.loginUserID}, constant.GetNewestSeq, &resp); err != nil {
+ log.ZError(m.ctx, "get max seq error", err)
+ return
} else {
- common.TriggerCmdNotification(m.ctx, sdk_struct.CmdNewMsgComeToConversation{SyncFlag: constant.MsgSyncEnd}, m.conversationCh)
+ log.ZDebug(m.ctx, "get max seq success", "resp", resp.MaxSeqs)
}
+ m.compareSeqsAndBatchSync(ctx, resp.MaxSeqs, defaultPullNums)
+}
+
+func (m *MsgSyncer) doIMMessageSync(ctx context.Context) {
+ var resp sdkws.GetMaxSeqResp
+ if err := m.longConnMgr.SendReqWaitResp(m.ctx, &sdkws.GetMaxSeqReq{UserID: m.loginUserID}, constant.GetNewestSeq, &resp); err != nil {
+ log.ZError(m.ctx, "get max seq error", err)
+ return
+ } else {
+ log.ZDebug(m.ctx, "get max seq success", "resp", resp.MaxSeqs)
+ }
+ m.compareSeqsAndBatchSync(ctx, resp.MaxSeqs, defaultPullNums)
}
func IsNotification(conversationID string) bool {
return strings.HasPrefix(conversationID, "n_")
}
-// Fragment synchronization message, seq refresh after successful trigger
func (m *MsgSyncer) syncAndTriggerMsgs(ctx context.Context, seqMap map[string][2]int64, syncMsgNum int64) error {
+ if len(seqMap) == 0 {
+ log.ZDebug(ctx, "nothing to sync", "syncMsgNum", syncMsgNum)
+ return nil
+ }
+
+ log.ZDebug(ctx, "current sync seqMap", "seqMap", seqMap)
+ var (
+ tempSeqMap = make(map[string][2]int64, 50)
+ msgNum = 0
+ )
+
+ for k, v := range seqMap {
+ oneConversationSyncNum := v[1] - v[0] + 1
+ tempSeqMap[k] = v
+ // For notification conversations, use oneConversationSyncNum directly
+ if IsNotification(k) {
+ msgNum += int(oneConversationSyncNum)
+ } else {
+ // For regular conversations, ensure msgNum is the minimum of oneConversationSyncNum and syncMsgNum
+ currentSyncMsgNum := int64(0)
+ if oneConversationSyncNum > syncMsgNum {
+ currentSyncMsgNum = syncMsgNum
+ } else {
+ currentSyncMsgNum = oneConversationSyncNum
+ }
+ msgNum += int(currentSyncMsgNum)
+ }
+
+ // If accumulated msgNum reaches SplitPullMsgNum, trigger a batch pull
+ if msgNum >= SplitPullMsgNum {
+ resp, err := m.pullMsgBySeqRange(ctx, tempSeqMap, syncMsgNum)
+ if err != nil {
+ log.ZError(ctx, "syncMsgFromServer error", err, "tempSeqMap", tempSeqMap)
+ return err
+ }
+ _ = m.triggerConversation(ctx, resp.Msgs)
+ _ = m.triggerNotification(ctx, resp.NotificationMsgs)
+ for conversationID, seqs := range tempSeqMap {
+ m.syncedMaxSeqs[conversationID] = seqs[1]
+ }
+ // Reset tempSeqMap and msgNum to handle the next batch
+ tempSeqMap = make(map[string][2]int64, 50)
+ msgNum = 0
+ }
+ }
+
+ // Handle remaining messages to ensure all are synced
+ if len(tempSeqMap) > 0 {
+ resp, err := m.pullMsgBySeqRange(ctx, tempSeqMap, syncMsgNum)
+ if err != nil {
+ log.ZError(ctx, "syncMsgFromServer error", err, "tempSeqMap", tempSeqMap)
+ return err
+ }
+ _ = m.triggerConversation(ctx, resp.Msgs)
+ _ = m.triggerNotification(ctx, resp.NotificationMsgs)
+ for conversationID, seqs := range tempSeqMap {
+ m.syncedMaxSeqs[conversationID] = seqs[1]
+ }
+ }
+
+ return nil
+}
+
+// Fragment synchronization message, seq refresh after successful trigger
+func (m *MsgSyncer) syncAndTriggerReinstallMsgs(ctx context.Context, seqMap map[string][2]int64, syncMsgNum int64) error {
if len(seqMap) > 0 {
log.ZDebug(ctx, "current sync seqMap", "seqMap", seqMap)
- tempSeqMap := make(map[string][2]int64, 50)
- msgNum := 0
+ var (
+ tempSeqMap = make(map[string][2]int64, 50)
+ msgNum = 0
+ total = len(seqMap)
+ gr *errgroup.Group
+ )
+ gr, _ = errgroup.WithContext(ctx)
+ gr.SetLimit(pullMsgGoroutineLimit)
for k, v := range seqMap {
- oneConversationSyncNum := v[1] - v[0] + 1
- if (oneConversationSyncNum/SplitPullMsgNum) > 1 && IsNotification(k) {
- nSeqMap := make(map[string][2]int64, 1)
- count := int(oneConversationSyncNum / SplitPullMsgNum)
- startSeq := v[0]
- var end int64
- for i := 0; i <= count; i++ {
- if i == count {
- nSeqMap[k] = [2]int64{startSeq, v[1]}
- } else {
- end = startSeq + int64(SplitPullMsgNum)
- if end > v[1] {
- end = v[1]
- i = count
- }
- nSeqMap[k] = [2]int64{startSeq, end}
- }
- resp, err := m.pullMsgBySeqRange(ctx, nSeqMap, syncMsgNum)
- if err != nil {
- log.ZError(ctx, "syncMsgFromSvr err", err, "nSeqMap", nSeqMap)
- return err
- }
- _ = m.triggerConversation(ctx, resp.Msgs)
- _ = m.triggerNotification(ctx, resp.NotificationMsgs)
- for conversationID, seqs := range nSeqMap {
- m.syncedMaxSeqs[conversationID] = seqs[1]
- }
- startSeq = end + 1
- }
- continue
- }
+ oneConversationSyncNum := min(v[1]-v[0]+1, syncMsgNum)
tempSeqMap[k] = v
if oneConversationSyncNum > 0 {
msgNum += int(oneConversationSyncNum)
}
if msgNum >= SplitPullMsgNum {
- resp, err := m.pullMsgBySeqRange(ctx, tempSeqMap, syncMsgNum)
- if err != nil {
- log.ZError(ctx, "syncMsgFromSvr err", err, "tempSeqMap", tempSeqMap)
- return err
- }
- _ = m.triggerConversation(ctx, resp.Msgs)
- _ = m.triggerNotification(ctx, resp.NotificationMsgs)
- for conversationID, seqs := range tempSeqMap {
- m.syncedMaxSeqs[conversationID] = seqs[1]
+ tpSeqMap := make(map[string][2]int64, len(tempSeqMap))
+ for k, v := range tempSeqMap {
+ tpSeqMap[k] = v
}
+
+ gr.Go(func() error {
+ resp, err := m.pullMsgBySeqRange(ctx, tpSeqMap, syncMsgNum)
+ if err != nil {
+ log.ZError(ctx, "syncMsgFromServer err", err, "tempSeqMap", tpSeqMap)
+ return err
+ }
+ m.checkMessagesAndGetLastMessage(ctx, resp.Msgs)
+ _ = m.triggerReinstallConversation(ctx, resp.Msgs, total)
+ for conversationID, seqs := range tpSeqMap {
+ m.syncedMaxSeqsLock.Lock()
+ m.syncedMaxSeqs[conversationID] = seqs[1]
+ m.syncedMaxSeqsLock.Unlock()
+ }
+ return nil
+ })
+
tempSeqMap = make(map[string][2]int64, 50)
msgNum = 0
}
}
-
- resp, err := m.pullMsgBySeqRange(ctx, tempSeqMap, syncMsgNum)
- if err != nil {
- log.ZError(ctx, "syncMsgFromSvr err", err, "seqMap", seqMap)
+ gr.Go(func() error {
+ resp, err := m.pullMsgBySeqRange(ctx, tempSeqMap, syncMsgNum)
+ if err != nil {
+ log.ZError(ctx, "syncMsgFromServer err", err, "seqMap", seqMap)
+ return err
+ }
+ m.checkMessagesAndGetLastMessage(ctx, resp.Msgs)
+ _ = m.triggerReinstallConversation(ctx, resp.Msgs, total)
+ for conversationID, seqs := range seqMap {
+ m.syncedMaxSeqsLock.Lock()
+ m.syncedMaxSeqs[conversationID] = seqs[1]
+ m.syncedMaxSeqsLock.Unlock()
+ }
+ return nil
+ })
+ if err := gr.Wait(); err != nil {
return err
}
- _ = m.triggerConversation(ctx, resp.Msgs)
- _ = m.triggerNotification(ctx, resp.NotificationMsgs)
- for conversationID, seqs := range seqMap {
- m.syncedMaxSeqs[conversationID] = seqs[1]
- }
+
} else {
log.ZDebug(ctx, "noting conversation to sync", "syncMsgNum", syncMsgNum)
}
return nil
}
+func (m *MsgSyncer) checkMessagesAndGetLastMessage(ctx context.Context, messages map[string]*sdkws.PullMsgs) {
+ var conversationIDs []string
+
+ for conversationID, message := range messages {
+ allInValid := true
+ for _, data := range message.Msgs {
+ if data.Status < constant.MsgStatusHasDeleted {
+ allInValid = false
+ break
+ }
+ }
+ if allInValid {
+ conversationIDs = append(conversationIDs, conversationID)
+ }
+ }
+ if len(conversationIDs) > 0 {
+ resp, err := m.fetchLatestValidMessages(ctx, conversationIDs)
+ if err != nil {
+ log.ZError(ctx, "fetchLatestValidMessages", err, "conversationIDs", conversationIDs)
+ return
+ }
+ for conversationID, message := range resp.Msgs {
+ messages[conversationID] = &sdkws.PullMsgs{Msgs: []*sdkws.MsgData{message}}
+ }
+ }
+
+}
func (m *MsgSyncer) splitSeqs(split int, seqsNeedSync []int64) (splitSeqs [][]int64) {
if len(seqsNeedSync) <= split {
@@ -392,7 +586,21 @@ func (m *MsgSyncer) pullMsgBySeqRange(ctx context.Context, seqMap map[string][2]
})
}
resp = &sdkws.PullMessageBySeqsResp{}
- if err := m.longConnMgr.SendReqWaitResp(ctx, &req, constant.PullMsgBySeqList, resp); err != nil {
+ if err := m.longConnMgr.SendReqWaitResp(ctx, &req, constant.PullMsgByRange, resp); err != nil {
+ return nil, err
+ }
+ return resp, nil
+}
+
+func (m *MsgSyncer) fetchLatestValidMessages(ctx context.Context, conversationID []string) (resp *msg.GetLastMessageResp, err error) {
+ log.ZDebug(ctx, "fetchLatestValidMessages", "conversationID", conversationID)
+
+ req := msg.GetLastMessageReq{
+ UserID: m.loginUserID,
+ ConversationIDs: conversationID,
+ }
+ resp = &msg.GetLastMessageResp{}
+ if err := m.longConnMgr.SendReqWaitResp(ctx, &req, constant.PullConvLastMessage, resp); err != nil {
return nil, err
}
return resp, nil
@@ -406,9 +614,9 @@ func (m *MsgSyncer) syncMsgBySeqs(ctx context.Context, conversationID string, se
seqsList := m.splitSeqs(split, seqsNeedSync)
for i := 0; i < len(seqsList); {
var pullMsgResp sdkws.PullMessageBySeqsResp
- err := m.longConnMgr.SendReqWaitResp(ctx, &pullMsgReq, constant.PullMsgBySeqList, &pullMsgResp)
+ err := m.longConnMgr.SendReqWaitResp(ctx, &pullMsgReq, constant.PullMsgByRange, &pullMsgResp)
if err != nil {
- log.ZError(ctx, "syncMsgFromSvrSplit err", err, "pullMsgReq", pullMsgReq)
+ log.ZError(ctx, "syncMsgFromServerSplit err", err, "pullMsgReq", pullMsgReq)
continue
}
i++
@@ -432,11 +640,29 @@ func (m *MsgSyncer) triggerConversation(ctx context.Context, msgs map[string]*sd
return nil
}
+// triggers a conversation with a new message.
+func (m *MsgSyncer) triggerReinstallConversation(ctx context.Context, msgs map[string]*sdkws.PullMsgs, total int) (err error) {
+ if len(msgs) > 0 {
+ err = common.TriggerCmdMsgSyncInReinstall(ctx, sdk_struct.CmdMsgSyncInReinstall{
+ Msgs: msgs,
+ Total: total,
+ }, m.conversationCh)
+ if err != nil {
+ log.ZError(ctx, "triggerCmdNewMsgCome err", err, "msgs", msgs)
+ }
+ log.ZDebug(ctx, "triggerConversation", "msgs", msgs)
+ return err
+ } else {
+ log.ZDebug(ctx, "triggerConversation is nil", "msgs", msgs)
+ }
+ return nil
+}
+
func (m *MsgSyncer) triggerNotification(ctx context.Context, msgs map[string]*sdkws.PullMsgs) error {
if len(msgs) > 0 {
common.TriggerCmdNotification(ctx, sdk_struct.CmdNewMsgComeToConversation{Msgs: msgs}, m.conversationCh)
} else {
- log.ZDebug(ctx, "triggerNotification is nil", "msgs", msgs)
+ log.ZDebug(ctx, "triggerNotification is nil", "notifications", msgs)
}
return nil
diff --git a/internal/interaction/online.go b/internal/interaction/online.go
new file mode 100644
index 000000000..225ce19f8
--- /dev/null
+++ b/internal/interaction/online.go
@@ -0,0 +1,46 @@
+package interaction
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ userPb "github.com/openimsdk/protocol/user"
+)
+
+func (c *LongConnMgr) subscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
+ if len(userIDs) == 0 {
+ return []*userPb.OnlineStatus{}, nil
+ }
+ res, err := c.GetUserOnlinePlatformIDs(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+ status := make([]*userPb.OnlineStatus, 0, len(res))
+ for userID, platformIDs := range res {
+ value := &userPb.OnlineStatus{
+ UserID: userID,
+ PlatformIDs: platformIDs,
+ }
+ if len(platformIDs) == 0 {
+ value.Status = constant.Offline
+ } else {
+ value.Status = constant.Online
+ }
+ status = append(status, value)
+ }
+ return status, nil
+}
+
+func (c *LongConnMgr) UnsubscribeUsersStatus(ctx context.Context, userIDs []string) error {
+ return c.UnsubscribeUserOnlinePlatformIDs(ctx, userIDs)
+}
+
+func (c *LongConnMgr) SubscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
+ if len(userIDs) == 0 {
+ return []*userPb.OnlineStatus{}, nil
+ }
+ return c.subscribeUsersStatus(ctx, userIDs)
+}
+
+func (c *LongConnMgr) GetSubscribeUsersStatus(ctx context.Context) ([]*userPb.OnlineStatus, error) {
+ return c.subscribeUsersStatus(ctx, nil)
+}
diff --git a/internal/interaction/subscription.go b/internal/interaction/subscription.go
new file mode 100644
index 000000000..b7e7d4fac
--- /dev/null
+++ b/internal/interaction/subscription.go
@@ -0,0 +1,207 @@
+package interaction
+
+import (
+ "errors"
+ "fmt"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/utils/datautil"
+ "slices"
+ "sync"
+ "unsafe"
+)
+
+type subscriptionStatues struct {
+ //state int8
+ //wait *subwait
+ done chan struct{}
+ err error
+ online []int32
+}
+
+func (s *subscriptionStatues) finish(online []int32, err error) {
+ select {
+ case <-s.done:
+ s.online = online
+ s.err = err
+ default:
+ s.online = online
+ s.err = err
+ close(s.done)
+ }
+}
+
+func (s *subscriptionStatues) Done() <-chan struct{} {
+ return s.done
+}
+
+func (s *subscriptionStatues) Result() ([]int32, error) {
+ return s.online, s.err
+}
+
+func newSubscription() *subscription {
+ return &subscription{
+ load: make(map[string]*subscriptionStatues),
+ unsub: make(map[string]struct{}),
+ sub: make(map[string]struct{}),
+ //done: make(chan struct{}),
+ }
+}
+
+type subscription struct {
+ lock sync.Mutex
+ load map[string]*subscriptionStatues
+ unsub map[string]struct{}
+ sub map[string]struct{}
+ //done chan struct{}
+ //err error
+}
+
+func (s *subscription) getNewConnSubUserIDs() []string {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ return datautil.Keys(s.sub)
+}
+
+func (s *subscription) onConnClosed(err error) {
+ if err == nil {
+ err = fmt.Errorf("connection closed")
+ }
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ clear(s.unsub)
+ for userID, statues := range s.load {
+ statues.finish(nil, err)
+ delete(s.load, userID)
+ }
+ //s.err = err
+ //close(s.done)
+ //s.done = make(chan struct{})
+}
+
+func (s *subscription) onConnSuccess() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ clear(s.unsub)
+ //s.err = nil
+ //close(s.done)
+}
+
+func (s *subscription) setUserState(changes []*sdkws.SubUserOnlineStatusElem) map[string][]int32 {
+ if len(changes) == 0 {
+ return nil
+ }
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ change := make(map[string][]int32)
+ for _, v := range changes {
+ if v.OnlinePlatformIDs == nil {
+ v.OnlinePlatformIDs = []int32{}
+ }
+ if status, ok := s.load[v.UserID]; ok {
+ delete(s.unsub, v.UserID)
+ if !slices.Equal(status.online, v.OnlinePlatformIDs) {
+ change[v.UserID] = v.OnlinePlatformIDs
+ }
+ status.finish(v.OnlinePlatformIDs, nil)
+ } else {
+ if _, ok := s.sub[v.UserID]; ok {
+ done := make(chan struct{})
+ s.load[v.UserID] = &subscriptionStatues{
+ done: done,
+ err: nil,
+ online: v.OnlinePlatformIDs,
+ }
+ change[v.UserID] = v.OnlinePlatformIDs
+ } else {
+ s.unsub[v.UserID] = struct{}{}
+ }
+ }
+ }
+ return change
+}
+
+func (s *subscription) unsubscribe(userIDs []string) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ for _, userID := range userIDs {
+ if _, ok := s.sub[userID]; ok {
+ delete(s.sub, userID)
+ s.unsub[userID] = struct{}{}
+ }
+ if status, ok := s.load[userID]; ok {
+ status.finish(nil, errors.New("unsubscribe"))
+ delete(s.load, userID)
+ }
+ }
+}
+
+func (s *subscription) getUserOnline(userIDs []string) (map[string][]int32, map[string]*subscriptionWait, []string, []string) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ if len(userIDs) == 0 {
+ userIDs = datautil.Keys(s.sub)
+ }
+ tmp := make(map[string]struct{})
+ exist := make(map[string][]int32)
+ wait := make(map[string]*subscriptionWait)
+ subUserIDs := make([]string, 0, len(userIDs))
+ for _, userID := range userIDs {
+ if _, ok := tmp[userID]; ok {
+ continue
+ }
+ tmp[userID] = struct{}{}
+ delete(s.unsub, userID)
+ if status, ok := s.load[userID]; ok {
+ select {
+ case <-status.Done():
+ exist[userID] = status.online
+ default:
+ wait[userID] = &subscriptionWait{status: status, first: false}
+ }
+ } else {
+ delete(s.unsub, userID)
+ status := &subscriptionStatues{
+ done: make(chan struct{}),
+ online: nil,
+ }
+ s.load[userID] = status
+ wait[userID] = &subscriptionWait{status: status, first: true}
+ subUserIDs = append(subUserIDs, userID)
+ }
+ }
+ if len(subUserIDs) == 0 {
+ return exist, wait, nil, nil
+ }
+ defer clear(s.unsub)
+ return exist, wait, subUserIDs, datautil.Keys(s.unsub)
+}
+
+func (s *subscription) writeFailed(waits map[string]*subscriptionWait, err error) {
+ if len(waits) == 0 {
+ return
+ }
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ for userID, wait := range waits {
+ if !wait.first {
+ continue
+ }
+ if status, ok := s.load[userID]; ok && uintptr(unsafe.Pointer(status)) == uintptr(unsafe.Pointer(wait.status)) {
+ delete(s.load, userID)
+ }
+ wait.status.finish(nil, err)
+ }
+}
+
+type subscriptionWait struct {
+ status *subscriptionStatues
+ first bool
+}
+
+func (s *subscriptionWait) Done() <-chan struct{} {
+ return s.status.done
+}
+
+func (s *subscriptionWait) Result() ([]int32, error) {
+ return s.status.online, s.status.err
+}
diff --git a/internal/interaction/subscription_test.go b/internal/interaction/subscription_test.go
new file mode 100644
index 000000000..5fb160186
--- /dev/null
+++ b/internal/interaction/subscription_test.go
@@ -0,0 +1,31 @@
+package interaction
+
+import (
+ "errors"
+ "testing"
+)
+
+func TestName(t *testing.T) {
+ sub := newSubscription()
+
+ //sub.setUserState([]*sdkws.SubUserOnlineStatusElem{
+ // {
+ // UserID: "1",
+ // OnlinePlatformIDs: []int32{1, 2, 3},
+ // },
+ // {
+ // UserID: "2",
+ // OnlinePlatformIDs: []int32{1},
+ // },
+ //})
+
+ exist, wait, subUserIDs, unsubUserIDs := sub.getUserOnline([]string{"1", "2", "3"})
+
+ t.Logf("exist: %v", exist)
+ t.Logf("wait: %v", wait)
+ t.Logf("subUserIDs: %v", subUserIDs)
+ t.Logf("unsubUserIDs: %v", unsubUserIDs)
+
+ sub.writeFailed(wait, errors.New("todo test"))
+
+}
diff --git a/internal/interaction/ws_default.go b/internal/interaction/ws_default.go
index 85a5bb0a8..4192eb10e 100644
--- a/internal/interaction/ws_default.go
+++ b/internal/interaction/ws_default.go
@@ -38,9 +38,7 @@ func (d *Default) SetWriteDeadline(timeout time.Duration) error {
}
func (d *Default) SetReadLimit(limit int64) {
- if !d.isSetConf {
- d.conn.SetReadLimit(limit)
- }
+ d.conn.SetReadLimit(limit)
}
@@ -49,10 +47,7 @@ func (d *Default) SetPingHandler(handler PingPongHandler) {
}
func (d *Default) SetPongHandler(handler PingPongHandler) {
- if !d.isSetConf {
- d.conn.SetPongHandler(handler)
- d.isSetConf = true
- }
+ d.conn.SetPongHandler(handler)
}
func (d *Default) LocalAddr() string {
diff --git a/internal/interaction/ws_js.go b/internal/interaction/ws_js.go
index 6cd376d3c..4676cee2b 100644
--- a/internal/interaction/ws_js.go
+++ b/internal/interaction/ws_js.go
@@ -71,6 +71,9 @@ func (w *JSWebSocket) Close() error {
}
func (w *JSWebSocket) WriteMessage(messageType int, message []byte) error {
+ if messageType == PingMessage || messageType == PongMessage {
+ return nil
+ }
return w.conn.Write(context.Background(), websocket.MessageType(messageType), message)
}
diff --git a/internal/interaction/ws_resp_asyn.go b/internal/interaction/ws_resp_asyn.go
index b798cb06c..518c0e41a 100644
--- a/internal/interaction/ws_resp_asyn.go
+++ b/internal/interaction/ws_resp_asyn.go
@@ -18,6 +18,7 @@ import (
"context"
"errors"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/tools/errs"
"sync"
"time"
@@ -58,14 +59,15 @@ func GenMsgIncr(userID string) string {
func (u *WsRespAsyn) AddCh(userID string) (string, chan *GeneralWsResp) {
u.wsMutex.Lock()
defer u.wsMutex.Unlock()
- msgIncr := GenMsgIncr(userID)
-
- ch := make(chan *GeneralWsResp, 1)
- _, ok := u.wsNotification[msgIncr]
- if ok {
+ for {
+ msgIncr := GenMsgIncr(userID)
+ ch := make(chan *GeneralWsResp, 1)
+ if _, ok := u.wsNotification[msgIncr]; ok {
+ continue
+ }
+ u.wsNotification[msgIncr] = ch
+ return msgIncr, ch
}
- u.wsNotification[msgIncr] = ch
- return msgIncr, ch
}
func (u *WsRespAsyn) AddChByIncr(msgIncr string) chan *GeneralWsResp {
@@ -120,7 +122,7 @@ func (u *WsRespAsyn) NotifyResp(ctx context.Context, wsResp GeneralWsResp) error
ch := u.GetCh(wsResp.MsgIncr)
if ch == nil {
- return utils.Wrap(errors.New("no ch"), "GetCh failed "+wsResp.MsgIncr)
+ return errs.WrapMsg(errors.New("no ch"), "GetCh failed "+wsResp.MsgIncr)
}
for {
err := u.notifyCh(ch, &wsResp, 1)
diff --git a/internal/relation/api.go b/internal/relation/api.go
new file mode 100644
index 000000000..0645f73a5
--- /dev/null
+++ b/internal/relation/api.go
@@ -0,0 +1,304 @@
+package relation
+
+import (
+ "context"
+
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/tools/utils/datautil"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/datafetcher"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
+
+ "github.com/openimsdk/tools/log"
+)
+
+func (r *Relation) GetSpecifiedFriendsInfo(ctx context.Context, friendUserIDList []string, filterBlack bool) ([]*model_struct.LocalFriend, error) {
+ dataFetcher := datafetcher.NewDataFetcher(
+ r.db,
+ r.friendListTableName(),
+ r.loginUserID,
+ func(localFriend *model_struct.LocalFriend) string {
+ return localFriend.FriendUserID
+ },
+ func(ctx context.Context, values []*model_struct.LocalFriend) error {
+ return r.db.BatchInsertFriend(ctx, values)
+ },
+ func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, bool, error) {
+ localFriends, err := r.db.GetFriendInfoList(ctx, userIDs)
+ return localFriends, true, err
+ },
+ func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, error) {
+ serverFriend, err := r.GetDesignatedFriends(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+ return datautil.Batch(ServerFriendToLocalFriend, serverFriend), nil
+ },
+ )
+ localFriendList, err := dataFetcher.FetchMissingAndFillLocal(ctx, friendUserIDList)
+ if err != nil {
+ return nil, err
+ }
+ if !filterBlack {
+ return localFriendList, nil
+ }
+ log.ZDebug(ctx, "GetDesignatedFriendsInfo", "localFriendList", localFriendList)
+ blackList, err := r.db.GetBlackInfoList(ctx, friendUserIDList)
+ if err != nil {
+ return nil, err
+ }
+ if len(blackList) == 0 {
+ return localFriendList, nil
+ }
+
+ log.ZDebug(ctx, "GetDesignatedFriendsInfo", "blackList", blackList)
+ m := datautil.SliceSetAny(blackList, func(e *model_struct.LocalBlack) string {
+ return e.BlockUserID
+ })
+ var res []*model_struct.LocalFriend
+ for _, localFriend := range localFriendList {
+ if _, ok := m[localFriend.FriendUserID]; !ok {
+ res = append(res, localFriend)
+ }
+ }
+ return res, nil
+}
+
+func (r *Relation) AddFriend(ctx context.Context, req *relation.ApplyToAddFriendReq) error {
+ if err := r.addFriend(ctx, req); err != nil {
+ return err
+ }
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ return r.SyncAllSelfFriendApplication(ctx)
+}
+
+func (r *Relation) GetFriendApplicationListAsRecipient(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
+ return r.db.GetRecvFriendApplication(ctx)
+}
+
+func (r *Relation) GetFriendApplicationListAsApplicant(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
+ return r.db.GetSendFriendApplication(ctx)
+}
+
+func (r *Relation) AcceptFriendApplication(ctx context.Context, userIDHandleMsg *sdk.ProcessFriendApplicationParams) error {
+ return r.RespondFriendApply(ctx, &relation.RespondFriendApplyReq{FromUserID: userIDHandleMsg.ToUserID, ToUserID: r.loginUserID, HandleResult: constant.FriendResponseAgree, HandleMsg: userIDHandleMsg.HandleMsg})
+}
+
+func (r *Relation) RefuseFriendApplication(ctx context.Context, userIDHandleMsg *sdk.ProcessFriendApplicationParams) error {
+ return r.RespondFriendApply(ctx, &relation.RespondFriendApplyReq{FromUserID: userIDHandleMsg.ToUserID, ToUserID: r.loginUserID, HandleResult: constant.FriendResponseRefuse, HandleMsg: userIDHandleMsg.HandleMsg})
+}
+
+func (r *Relation) RespondFriendApply(ctx context.Context, req *relation.RespondFriendApplyReq) error {
+ if err := r.addFriendResponse(ctx, req); err != nil {
+ return err
+ }
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ if req.HandleResult == constant.FriendResponseAgree {
+ _ = r.IncrSyncFriends(ctx)
+ }
+ _ = r.SyncAllFriendApplication(ctx)
+ return nil
+ // return r.SyncFriendApplication(ctx)
+}
+
+func (r *Relation) CheckFriend(ctx context.Context, friendUserIDList []string) ([]*server_api_params.UserIDResult, error) {
+ friendList, err := r.db.GetFriendInfoList(ctx, friendUserIDList)
+ if err != nil {
+ return nil, err
+ }
+ blackList, err := r.db.GetBlackInfoList(ctx, friendUserIDList)
+ if err != nil {
+ return nil, err
+ }
+ res := make([]*server_api_params.UserIDResult, 0, len(friendUserIDList))
+ for _, v := range friendUserIDList {
+ var r server_api_params.UserIDResult
+ isBlack := false
+ isFriend := false
+ for _, b := range blackList {
+ if v == b.BlockUserID {
+ isBlack = true
+ break
+ }
+ }
+ for _, r := range friendList {
+ if v == r.FriendUserID {
+ isFriend = true
+ break
+ }
+ }
+ r.UserID = v
+ if isFriend && !isBlack {
+ r.Result = 1
+ } else {
+ r.Result = 0
+ }
+ res = append(res, &r)
+ }
+ return res, nil
+}
+
+func (r *Relation) DeleteFriend(ctx context.Context, friendUserID string) error {
+ if err := r.deleteFriend(ctx, friendUserID); err != nil {
+ return err
+ }
+
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ return r.IncrSyncFriends(ctx)
+}
+
+func (r *Relation) GetFriendList(ctx context.Context, filterBlack bool) ([]*model_struct.LocalFriend, error) {
+ localFriendList, err := r.db.GetAllFriendList(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if len(localFriendList) == 0 || !filterBlack {
+ return localFriendList, nil
+ }
+ localBlackList, err := r.db.GetBlackListDB(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if len(localBlackList) == 0 {
+ return localFriendList, nil
+ }
+ blackSet := datautil.SliceSetAny(localBlackList, func(e *model_struct.LocalBlack) string {
+ return e.BlockUserID
+ })
+ var res []*model_struct.LocalFriend
+ for _, friend := range localFriendList {
+ if _, ok := blackSet[friend.FriendUserID]; !ok {
+ res = append(res, friend)
+ }
+ }
+ return res, nil
+}
+
+func (r *Relation) GetFriendListPage(ctx context.Context, offset, count int32, filterBlack bool) ([]*model_struct.LocalFriend, error) {
+ dataFetcher := datafetcher.NewDataFetcher(
+ r.db,
+ r.friendListTableName(),
+ r.loginUserID,
+ func(localFriend *model_struct.LocalFriend) string {
+ return localFriend.FriendUserID
+ },
+ func(ctx context.Context, values []*model_struct.LocalFriend) error {
+ return r.db.BatchInsertFriend(ctx, values)
+ },
+ func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, bool, error) {
+ localFriendList, err := r.db.GetFriendInfoList(ctx, userIDs)
+ return localFriendList, true, err
+ },
+ func(ctx context.Context, userIDs []string) ([]*model_struct.LocalFriend, error) {
+ serverFriend, err := r.GetDesignatedFriends(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+ return datautil.Batch(ServerFriendToLocalFriend, serverFriend), nil
+ },
+ )
+ localBlackList, err := r.db.GetBlackListDB(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if (!filterBlack) || len(localBlackList) == 0 {
+ return dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
+ }
+ localFriendList, err := dataFetcher.FetchWithPagination(ctx, int(offset), int(count*2))
+ if err != nil {
+ return nil, err
+ }
+ blackUserIDs := datautil.SliceSetAny(localBlackList, func(e *model_struct.LocalBlack) string {
+ return e.BlockUserID
+ })
+ res := localFriendList[:0]
+ for _, friend := range localFriendList {
+ if _, ok := blackUserIDs[friend.FriendUserID]; !ok {
+ res = append(res, friend)
+ }
+ if len(res) == int(count) {
+ break
+ }
+ }
+ return res, nil
+}
+
+func (r *Relation) SearchFriends(ctx context.Context, param *sdk.SearchFriendsParam) ([]*sdk.SearchFriendItem, error) {
+ if len(param.KeywordList) == 0 || (!param.IsSearchNickname && !param.IsSearchUserID && !param.IsSearchRemark) {
+ return nil, sdkerrs.ErrArgs.WrapMsg("keyword is null or search field all false")
+ }
+ localFriendList, err := r.db.SearchFriendList(ctx, param.KeywordList[0], param.IsSearchUserID, param.IsSearchNickname, param.IsSearchRemark)
+ if err != nil {
+ return nil, err
+ }
+ localBlackList, err := r.db.GetBlackListDB(ctx)
+ if err != nil {
+ return nil, err
+ }
+ m := make(map[string]struct{})
+ for _, black := range localBlackList {
+ m[black.BlockUserID] = struct{}{}
+ }
+ res := make([]*sdk.SearchFriendItem, 0, len(localFriendList))
+ for i, localFriend := range localFriendList {
+ var relationship int
+ if _, ok := m[localFriend.FriendUserID]; ok {
+ relationship = constant.BlackRelationship
+ } else {
+ relationship = constant.FriendRelationship
+ }
+ res = append(res, &sdk.SearchFriendItem{
+ LocalFriend: *localFriendList[i],
+ Relationship: relationship,
+ })
+ }
+ return res, nil
+}
+
+func (r *Relation) AddBlack(ctx context.Context, blackUserID string, ex string) error {
+ if err := r.addBlack(ctx, &relation.AddBlackReq{BlackUserID: blackUserID, Ex: ex}); err != nil {
+ return err
+ }
+
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ return r.SyncAllBlackList(ctx)
+}
+
+func (r *Relation) RemoveBlack(ctx context.Context, blackUserID string) error {
+ if err := r.removeBlack(ctx, blackUserID); err != nil {
+ return err
+ }
+
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ return r.SyncAllBlackList(ctx)
+}
+
+func (r *Relation) GetBlackList(ctx context.Context) ([]*model_struct.LocalBlack, error) {
+ return r.db.GetBlackListDB(ctx)
+}
+
+func (r *Relation) UpdateFriends(ctx context.Context, req *relation.UpdateFriendsReq) error {
+ req.OwnerUserID = r.loginUserID
+ if err := r.updateFriends(ctx, req); err != nil {
+ return err
+ }
+
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
+
+ return r.IncrSyncFriends(ctx)
+}
diff --git a/internal/friend/conversion.go b/internal/relation/conversion.go
similarity index 74%
rename from internal/friend/conversion.go
rename to internal/relation/conversion.go
index 0466bb449..de6098197 100644
--- a/internal/friend/conversion.go
+++ b/internal/relation/conversion.go
@@ -1,18 +1,4 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package friend
+package relation
import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
diff --git a/internal/relation/incremental_sync.go b/internal/relation/incremental_sync.go
new file mode 100644
index 000000000..b4fe878e6
--- /dev/null
+++ b/internal/relation/incremental_sync.go
@@ -0,0 +1,70 @@
+package relation
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+func (r *Relation) IncrSyncFriends(ctx context.Context) error {
+ friendSyncer := syncer.VersionSynchronizer[*model_struct.LocalFriend, *relation.GetIncrementalFriendsResp]{
+ Ctx: ctx,
+ DB: r.db,
+ TableName: r.friendListTableName(),
+ EntityID: r.loginUserID,
+ Key: func(localFriend *model_struct.LocalFriend) string {
+ return localFriend.FriendUserID
+ },
+ Local: func() ([]*model_struct.LocalFriend, error) {
+ return r.db.GetAllFriendList(ctx)
+ },
+ Server: func(version *model_struct.LocalVersionSync) (*relation.GetIncrementalFriendsResp, error) {
+ return r.getIncrementalFriends(ctx, &relation.GetIncrementalFriendsReq{
+ UserID: r.loginUserID,
+ Version: version.Version,
+ VersionID: version.VersionID,
+ })
+ },
+ Full: func(resp *relation.GetIncrementalFriendsResp) bool {
+ return resp.Full
+ },
+ Version: func(resp *relation.GetIncrementalFriendsResp) (string, uint64) {
+ return resp.VersionID, resp.Version
+ },
+ Delete: func(resp *relation.GetIncrementalFriendsResp) []string {
+ return resp.Delete
+ },
+ Update: func(resp *relation.GetIncrementalFriendsResp) []*model_struct.LocalFriend {
+ return datautil.Batch(ServerFriendToLocalFriend, resp.Update)
+ },
+ Insert: func(resp *relation.GetIncrementalFriendsResp) []*model_struct.LocalFriend {
+ return datautil.Batch(ServerFriendToLocalFriend, resp.Insert)
+ },
+ Syncer: func(server, local []*model_struct.LocalFriend) error {
+ return r.friendSyncer.Sync(ctx, server, local, nil)
+ },
+ FullSyncer: func(ctx context.Context) error {
+ return r.friendSyncer.FullSync(ctx, r.loginUserID)
+ },
+ FullID: func(ctx context.Context) ([]string, error) {
+ resp, err := r.getFullFriendUserIDs(ctx, &relation.GetFullFriendUserIDsReq{
+ UserID: r.loginUserID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return resp.UserIDs, nil
+ },
+ IDOrderChanged: func(resp *relation.GetIncrementalFriendsResp) bool {
+ return resp.SortVersion > 0
+ },
+ }
+ return friendSyncer.IncrementalSync()
+}
+
+func (r *Relation) friendListTableName() string {
+ return model_struct.LocalFriend{}.TableName()
+}
diff --git a/internal/friend/notification.go b/internal/relation/notification.go
similarity index 56%
rename from internal/friend/notification.go
rename to internal/relation/notification.go
index ab5c8c484..80df2bba6 100644
--- a/internal/friend/notification.go
+++ b/internal/relation/notification.go
@@ -1,18 +1,4 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package friend
+package relation
import (
"context"
@@ -24,17 +10,17 @@ import (
"github.com/openimsdk/tools/log"
)
-func (f *Friend) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
+func (r *Relation) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
go func() {
- if err := f.doNotification(ctx, msg); err != nil {
+ if err := r.doNotification(ctx, msg); err != nil {
log.ZError(ctx, "doNotification error", err, "msg", msg)
}
}()
}
-func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
- f.friendSyncMutex.Lock()
- defer f.friendSyncMutex.Unlock()
+func (r *Relation) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ r.relationSyncMutex.Lock()
+ defer r.relationSyncMutex.Unlock()
switch msg.ContentType {
case constant.FriendApplicationNotification:
@@ -42,7 +28,7 @@ func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- return f.SyncBothFriendRequest(ctx,
+ return r.SyncBothFriendRequest(ctx,
tips.FromToUserID.FromUserID, tips.FromToUserID.ToUserID)
case constant.FriendApplicationApprovedNotification:
var tips sdkws.FriendApplicationApprovedTips
@@ -51,31 +37,31 @@ func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
return err
}
- if tips.FromToUserID.FromUserID == f.loginUserID {
- err = f.IncrSyncFriends(ctx)
- } else if tips.FromToUserID.ToUserID == f.loginUserID {
- err = f.IncrSyncFriends(ctx)
+ if tips.FromToUserID.FromUserID == r.loginUserID {
+ err = r.IncrSyncFriends(ctx)
+ } else if tips.FromToUserID.ToUserID == r.loginUserID {
+ err = r.IncrSyncFriends(ctx)
}
if err != nil {
return err
}
- return f.SyncBothFriendRequest(ctx, tips.FromToUserID.FromUserID, tips.FromToUserID.ToUserID)
+ return r.SyncBothFriendRequest(ctx, tips.FromToUserID.FromUserID, tips.FromToUserID.ToUserID)
case constant.FriendApplicationRejectedNotification:
var tips sdkws.FriendApplicationRejectedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- return f.SyncBothFriendRequest(ctx, tips.FromToUserID.FromUserID, tips.FromToUserID.ToUserID)
+ return r.SyncBothFriendRequest(ctx, tips.FromToUserID.FromUserID, tips.FromToUserID.ToUserID)
case constant.FriendAddedNotification:
var tips sdkws.FriendAddedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
if tips.Friend != nil && tips.Friend.FriendUser != nil {
- if tips.Friend.FriendUser.UserID == f.loginUserID {
- return f.IncrSyncFriends(ctx)
- } else if tips.Friend.OwnerUserID == f.loginUserID {
- return f.IncrSyncFriends(ctx)
+ if tips.Friend.FriendUser.UserID == r.loginUserID {
+ return r.IncrSyncFriends(ctx)
+ } else if tips.Friend.OwnerUserID == r.loginUserID {
+ return r.IncrSyncFriends(ctx)
}
}
case constant.FriendDeletedNotification:
@@ -84,8 +70,8 @@ func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
return err
}
if tips.FromToUserID != nil {
- if tips.FromToUserID.FromUserID == f.loginUserID {
- return f.IncrSyncFriends(ctx)
+ if tips.FromToUserID.FromUserID == r.loginUserID {
+ return r.IncrSyncFriends(ctx)
}
}
case constant.FriendRemarkSetNotification:
@@ -94,8 +80,8 @@ func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
return err
}
if tips.FromToUserID != nil {
- if tips.FromToUserID.FromUserID == f.loginUserID {
- return f.IncrSyncFriends(ctx)
+ if tips.FromToUserID.FromUserID == r.loginUserID {
+ return r.IncrSyncFriends(ctx)
}
}
case constant.FriendInfoUpdatedNotification:
@@ -103,34 +89,32 @@ func (f *Friend) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- if tips.UserID != f.loginUserID {
- return f.IncrSyncFriends(ctx)
+ if tips.UserID != r.loginUserID {
+ return r.IncrSyncFriends(ctx)
}
case constant.BlackAddedNotification:
var tips sdkws.BlackAddedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- if tips.FromToUserID.FromUserID == f.loginUserID {
- return f.SyncAllBlackList(ctx)
+ if tips.FromToUserID.FromUserID == r.loginUserID {
+ return r.SyncAllBlackList(ctx)
}
case constant.BlackDeletedNotification:
var tips sdkws.BlackDeletedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- if tips.FromToUserID.FromUserID == f.loginUserID {
- return f.SyncAllBlackList(ctx)
+ if tips.FromToUserID.FromUserID == r.loginUserID {
+ return r.SyncAllBlackList(ctx)
}
case constant.FriendsInfoUpdateNotification:
-
var tips sdkws.FriendsInfoUpdateTips
-
if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
return err
}
- if tips.FromToUserID.ToUserID == f.loginUserID {
- return f.IncrSyncFriends(ctx)
+ if tips.FromToUserID.ToUserID == r.loginUserID {
+ return r.IncrSyncFriends(ctx)
}
default:
return fmt.Errorf("type failed %d", msg.ContentType)
diff --git a/internal/friend/friend.go b/internal/relation/relation.go
similarity index 64%
rename from internal/friend/friend.go
rename to internal/relation/relation.go
index 5e871a74a..8e4be53ea 100644
--- a/internal/friend/friend.go
+++ b/internal/relation/relation.go
@@ -1,18 +1,4 @@
-// Copyright 2021 OpenIM Corporation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package friend
+package relation
import (
"context"
@@ -20,6 +6,7 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/internal/user"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
@@ -34,39 +21,43 @@ import (
)
const (
- friendSyncLimit = 100
+ friendSyncLimit int64 = 10000
)
-func NewFriend(loginUserID string, db db_interface.DataBase, user *user.User, conversationCh chan common.Cmd2Value) *Friend {
- f := &Friend{loginUserID: loginUserID, db: db, user: user, conversationCh: conversationCh}
- f.initSyncer()
- return f
+func NewFriend(loginUserID string, db db_interface.DataBase, user *user.User, conversationCh chan common.Cmd2Value) *Relation {
+ r := &Relation{loginUserID: loginUserID, db: db, user: user, conversationCh: conversationCh}
+ r.initSyncer()
+ return r
}
-type Friend struct {
- friendListener open_im_sdk_callback.OnFriendshipListenerSdk
+type Relation struct {
+ friendshipListener open_im_sdk_callback.OnFriendshipListenerSdk
loginUserID string
db db_interface.DataBase
user *user.User
friendSyncer *syncer.Syncer[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string]
- blockSyncer *syncer.Syncer[*model_struct.LocalBlack, syncer.NoResp, [2]string]
+ blackSyncer *syncer.Syncer[*model_struct.LocalBlack, syncer.NoResp, [2]string]
requestRecvSyncer *syncer.Syncer[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string]
requestSendSyncer *syncer.Syncer[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string]
conversationCh chan common.Cmd2Value
listenerForService open_im_sdk_callback.OnListenerForService
- friendSyncMutex sync.Mutex
+ relationSyncMutex sync.Mutex
+
+ requestRecvSyncerLock sync.Mutex
+ requestSendSyncerLock sync.Mutex
}
-func (f *Friend) initSyncer() {
- f.friendSyncer = syncer.New2[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](
+func (r *Relation) initSyncer() {
+ r.friendSyncer = syncer.New2[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](
syncer.WithInsert[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriend) error {
- return f.db.InsertFriend(ctx, value)
+ return r.db.InsertFriend(ctx, value)
}),
syncer.WithDelete[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriend) error {
- return f.db.DeleteFriendDB(ctx, value.FriendUserID)
+ return r.db.DeleteFriendDB(ctx, value.FriendUserID)
}),
syncer.WithUpdate[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, server, local *model_struct.LocalFriend) error {
- return f.db.UpdateFriend(ctx, server)
+ r.user.UserCache.Delete(server.FriendUserID)
+ return r.db.UpdateFriend(ctx, server)
}),
syncer.WithUUID[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(value *model_struct.LocalFriend) [2]string {
return [...]string{value.OwnerUserID, value.FriendUserID}
@@ -74,12 +65,33 @@ func (f *Friend) initSyncer() {
syncer.WithNotice[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, state int, server, local *model_struct.LocalFriend) error {
switch state {
case syncer.Insert:
- f.friendListener.OnFriendAdded(*server)
+ r.friendshipListener.OnFriendAdded(*server)
+ if server.Remark != "" {
+ server.Nickname = server.Remark
+ }
+ _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{
+ Action: constant.UpdateConFaceUrlAndNickName,
+ Args: common.SourceIDAndSessionType{
+ SourceID: server.FriendUserID,
+ SessionType: constant.SingleChatType,
+ FaceURL: server.FaceURL,
+ Nickname: server.Nickname,
+ },
+ }, r.conversationCh)
+ _ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{
+ Action: constant.UpdateMsgFaceUrlAndNickName,
+ Args: common.UpdateMessageInfo{
+ SessionType: constant.SingleChatType,
+ UserID: server.FriendUserID,
+ FaceURL: server.FaceURL,
+ Nickname: server.Nickname,
+ },
+ }, r.conversationCh)
case syncer.Delete:
log.ZDebug(ctx, "syncer OnFriendDeleted", "local", local)
- f.friendListener.OnFriendDeleted(*local)
+ r.friendshipListener.OnFriendDeleted(*local)
case syncer.Update:
- f.friendListener.OnFriendInfoChanged(*server)
+ r.friendshipListener.OnFriendInfoChanged(*server)
if local.Nickname != server.Nickname || local.FaceURL != server.FaceURL || local.Remark != server.Remark {
if server.Remark != "" {
server.Nickname = server.Remark
@@ -92,7 +104,7 @@ func (f *Friend) initSyncer() {
FaceURL: server.FaceURL,
Nickname: server.Nickname,
},
- }, f.conversationCh)
+ }, r.conversationCh)
_ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{
Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{
@@ -101,17 +113,17 @@ func (f *Friend) initSyncer() {
FaceURL: server.FaceURL,
Nickname: server.Nickname,
},
- }, f.conversationCh)
+ }, r.conversationCh)
}
}
return nil
}),
syncer.WithBatchInsert[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, values []*model_struct.LocalFriend) error {
log.ZDebug(ctx, "BatchInsertFriend", "length", len(values))
- return f.db.BatchInsertFriend(ctx, values)
+ return r.db.BatchInsertFriend(ctx, values)
}),
syncer.WithDeleteAll[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(ctx context.Context, _ string) error {
- return f.db.DeleteAllFriend(ctx)
+ return r.db.DeleteAllFriend(ctx)
}),
syncer.WithBatchPageReq[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(entityID string) page.PageReq {
return &relation.GetPaginationFriendsReq{UserID: entityID,
@@ -120,87 +132,87 @@ func (f *Friend) initSyncer() {
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](func(resp *relation.GetPaginationFriendsResp) []*model_struct.LocalFriend {
return datautil.Batch(ServerFriendToLocalFriend, resp.FriendsInfo)
}),
- syncer.WithReqApiRouter[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](constant.GetFriendListRouter),
+ syncer.WithReqApiRouter[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](api.GetFriendList.Route()),
syncer.WithFullSyncLimit[*model_struct.LocalFriend, relation.GetPaginationFriendsResp, [2]string](friendSyncLimit),
)
- f.blockSyncer = syncer.New[*model_struct.LocalBlack, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalBlack) error {
- return f.db.InsertBlack(ctx, value)
+ r.blackSyncer = syncer.New[*model_struct.LocalBlack, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalBlack) error {
+ return r.db.InsertBlack(ctx, value)
}, func(ctx context.Context, value *model_struct.LocalBlack) error {
- return f.db.DeleteBlack(ctx, value.BlockUserID)
+ return r.db.DeleteBlack(ctx, value.BlockUserID)
}, func(ctx context.Context, server *model_struct.LocalBlack, local *model_struct.LocalBlack) error {
- return f.db.UpdateBlack(ctx, server)
+ return r.db.UpdateBlack(ctx, server)
}, func(value *model_struct.LocalBlack) [2]string {
return [...]string{value.OwnerUserID, value.BlockUserID}
}, nil, func(ctx context.Context, state int, server, local *model_struct.LocalBlack) error {
switch state {
case syncer.Insert:
- f.friendListener.OnBlackAdded(*server)
+ r.friendshipListener.OnBlackAdded(*server)
case syncer.Delete:
- f.friendListener.OnBlackDeleted(*local)
+ r.friendshipListener.OnBlackDeleted(*local)
}
return nil
})
- f.requestRecvSyncer = syncer.New[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
- return f.db.InsertFriendRequest(ctx, value)
+ r.requestRecvSyncer = syncer.New[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
+ return r.db.InsertFriendRequest(ctx, value)
}, func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
- return f.db.DeleteFriendRequestBothUserID(ctx, value.FromUserID, value.ToUserID)
+ return r.db.DeleteFriendRequestBothUserID(ctx, value.FromUserID, value.ToUserID)
}, func(ctx context.Context, server *model_struct.LocalFriendRequest, local *model_struct.LocalFriendRequest) error {
- return f.db.UpdateFriendRequest(ctx, server)
+ return r.db.UpdateFriendRequest(ctx, server)
}, func(value *model_struct.LocalFriendRequest) [2]string {
return [...]string{value.FromUserID, value.ToUserID}
}, nil, func(ctx context.Context, state int, server, local *model_struct.LocalFriendRequest) error {
switch state {
case syncer.Insert:
- f.friendListener.OnFriendApplicationAdded(*server)
+ r.friendshipListener.OnFriendApplicationAdded(*server)
case syncer.Delete:
- f.friendListener.OnFriendApplicationDeleted(*local)
+ r.friendshipListener.OnFriendApplicationDeleted(*local)
case syncer.Update:
switch server.HandleResult {
case constant.FriendResponseAgree:
- f.friendListener.OnFriendApplicationAccepted(*server)
+ r.friendshipListener.OnFriendApplicationAccepted(*server)
case constant.FriendResponseRefuse:
- f.friendListener.OnFriendApplicationRejected(*server)
+ r.friendshipListener.OnFriendApplicationRejected(*server)
case constant.FriendResponseDefault:
- f.friendListener.OnFriendApplicationAdded(*server)
+ r.friendshipListener.OnFriendApplicationAdded(*server)
}
}
return nil
})
- f.requestSendSyncer = syncer.New[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
- return f.db.InsertFriendRequest(ctx, value)
+ r.requestSendSyncer = syncer.New[*model_struct.LocalFriendRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
+ return r.db.InsertFriendRequest(ctx, value)
}, func(ctx context.Context, value *model_struct.LocalFriendRequest) error {
- return f.db.DeleteFriendRequestBothUserID(ctx, value.FromUserID, value.ToUserID)
+ return r.db.DeleteFriendRequestBothUserID(ctx, value.FromUserID, value.ToUserID)
}, func(ctx context.Context, server *model_struct.LocalFriendRequest, local *model_struct.LocalFriendRequest) error {
- return f.db.UpdateFriendRequest(ctx, server)
+ return r.db.UpdateFriendRequest(ctx, server)
}, func(value *model_struct.LocalFriendRequest) [2]string {
return [...]string{value.FromUserID, value.ToUserID}
}, nil, func(ctx context.Context, state int, server, local *model_struct.LocalFriendRequest) error {
switch state {
case syncer.Insert:
- f.friendListener.OnFriendApplicationAdded(*server)
+ r.friendshipListener.OnFriendApplicationAdded(*server)
case syncer.Delete:
- f.friendListener.OnFriendApplicationDeleted(*local)
+ r.friendshipListener.OnFriendApplicationDeleted(*local)
case syncer.Update:
switch server.HandleResult {
case constant.FriendResponseAgree:
- f.friendListener.OnFriendApplicationAccepted(*server)
+ r.friendshipListener.OnFriendApplicationAccepted(*server)
case constant.FriendResponseRefuse:
- f.friendListener.OnFriendApplicationRejected(*server)
+ r.friendshipListener.OnFriendApplicationRejected(*server)
}
}
return nil
})
}
-func (f *Friend) Db() db_interface.DataBase {
- return f.db
+func (r *Relation) Db() db_interface.DataBase {
+ return r.db
}
-func (f *Friend) SetListener(listener func() open_im_sdk_callback.OnFriendshipListener) {
- f.friendListener = open_im_sdk_callback.NewOnFriendshipListenerSdk(listener)
+func (r *Relation) SetListener(listener func() open_im_sdk_callback.OnFriendshipListener) {
+ r.friendshipListener = open_im_sdk_callback.NewOnFriendshipListenerSdk(listener)
}
-func (f *Friend) SetListenerForService(listener open_im_sdk_callback.OnListenerForService) {
- f.listenerForService = listener
+func (r *Relation) SetListenerForService(listener open_im_sdk_callback.OnListenerForService) {
+ r.listenerForService = listener
}
diff --git a/internal/relation/server_api.go b/internal/relation/server_api.go
new file mode 100644
index 000000000..6f520368e
--- /dev/null
+++ b/internal/relation/server_api.go
@@ -0,0 +1,71 @@
+package relation
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/protocol/sdkws"
+)
+
+func (r *Relation) getDesignatedFriendsApply(ctx context.Context, req *relation.GetDesignatedFriendsApplyReq) ([]*sdkws.FriendRequest, error) {
+ return api.ExtractField(ctx, api.GetDesignatedFriendsApply.Invoke, req, (*relation.GetDesignatedFriendsApplyResp).GetFriendRequests)
+}
+
+func (r *Relation) getSelfFriendApplicationList(ctx context.Context, req *relation.GetPaginationFriendsApplyFromReq) ([]*sdkws.FriendRequest, error) {
+ return api.Page(ctx, req, api.GetSelfFriendApplicationList.Invoke, (*relation.GetPaginationFriendsApplyFromResp).GetFriendRequests)
+}
+
+func (r *Relation) getFriendApplicationList(ctx context.Context, req *relation.GetPaginationFriendsApplyToReq) ([]*sdkws.FriendRequest, error) {
+ return api.Page(ctx, req, api.GetFriendApplicationList.Invoke, (*relation.GetPaginationFriendsApplyToResp).GetFriendRequests)
+}
+
+func (r *Relation) getBlackList(ctx context.Context) ([]*sdkws.BlackInfo, error) {
+ req := &relation.GetPaginationBlacksReq{UserID: r.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ return api.Page(ctx, req, api.GetBlackList.Invoke, (*relation.GetPaginationBlacksResp).GetBlacks)
+}
+
+func (r *Relation) getDesignatedFriends(ctx context.Context, friendIDs []string) ([]*sdkws.FriendInfo, error) {
+ req := &relation.GetDesignatedFriendsReq{OwnerUserID: r.loginUserID, FriendUserIDs: friendIDs}
+ return api.ExtractField(ctx, api.GetDesignatedFriends.Invoke, req, (*relation.GetDesignatedFriendsResp).GetFriendsInfo)
+}
+
+func (r *Relation) getIncrementalFriends(ctx context.Context, req *relation.GetIncrementalFriendsReq) (*relation.GetIncrementalFriendsResp, error) {
+ return api.GetIncrementalFriends.Invoke(ctx, req)
+}
+
+func (r *Relation) getFullFriendUserIDs(ctx context.Context, req *relation.GetFullFriendUserIDsReq) (*relation.GetFullFriendUserIDsResp, error) {
+ return api.GetFullFriendUserIDs.Invoke(ctx, req)
+}
+
+func (r *Relation) addFriend(ctx context.Context, req *relation.ApplyToAddFriendReq) error {
+ req.FromUserID = r.loginUserID
+ return api.AddFriend.Execute(ctx, req)
+}
+
+func (r *Relation) deleteFriend(ctx context.Context, friendUserID string) error {
+ req := &relation.DeleteFriendReq{OwnerUserID: r.loginUserID, FriendUserID: friendUserID}
+ return api.DeleteFriend.Execute(ctx, req)
+}
+
+func (r *Relation) addFriendResponse(ctx context.Context, req *relation.RespondFriendApplyReq) error {
+ req.ToUserID = r.loginUserID
+ return api.AddFriendResponse.Execute(ctx, req)
+}
+
+func (r *Relation) updateFriends(ctx context.Context, req *relation.UpdateFriendsReq) error {
+ req.OwnerUserID = r.loginUserID
+ return api.UpdateFriends.Execute(ctx, req)
+}
+
+func (r *Relation) addBlack(ctx context.Context, req *relation.AddBlackReq) error {
+ req.OwnerUserID = r.loginUserID
+ return api.AddBlack.Execute(ctx, req)
+}
+
+func (r *Relation) removeBlack(ctx context.Context, userID string) error {
+ return api.RemoveBlack.Execute(ctx, &relation.RemoveBlackReq{
+ OwnerUserID: r.loginUserID,
+ BlackUserID: userID,
+ })
+}
diff --git a/internal/relation/sync.go b/internal/relation/sync.go
new file mode 100644
index 000000000..175b9a08b
--- /dev/null
+++ b/internal/relation/sync.go
@@ -0,0 +1,153 @@
+package relation
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/openimsdk/tools/utils/datautil"
+
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/log"
+)
+
+func (r *Relation) SyncBothFriendRequest(ctx context.Context, fromUserID, toUserID string) error {
+ if toUserID == r.loginUserID {
+ if !r.requestRecvSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestRecvSyncerLock.Unlock()
+ } else {
+ if !r.requestSendSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestSendSyncerLock.Unlock()
+ }
+ req := &relation.GetDesignatedFriendsApplyReq{FromUserID: fromUserID, ToUserID: toUserID}
+ friendRequests, err := r.getDesignatedFriendsApply(ctx, req)
+ if err != nil {
+ return err
+ }
+ localData, err := r.db.GetBothFriendReq(ctx, fromUserID, toUserID)
+ if err != nil {
+ return err
+ }
+ if toUserID == r.loginUserID {
+ return r.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, friendRequests), localData, nil)
+ } else if fromUserID == r.loginUserID {
+ return r.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, friendRequests), localData, nil)
+ }
+ return nil
+}
+
+// SyncAllSelfFriendApplication send
+func (r *Relation) SyncAllSelfFriendApplication(ctx context.Context) error {
+ if !r.requestSendSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestSendSyncerLock.Unlock()
+ req := &relation.GetPaginationFriendsApplyFromReq{UserID: r.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ requests, err := r.getSelfFriendApplicationList(ctx, req)
+ if err != nil {
+ return err
+ }
+ localData, err := r.db.GetSendFriendApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return r.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil)
+}
+
+func (r *Relation) SyncAllSelfFriendApplicationWithoutNotice(ctx context.Context) error {
+ if !r.requestSendSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestSendSyncerLock.Unlock()
+ req := &relation.GetPaginationFriendsApplyFromReq{UserID: r.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ requests, err := r.getSelfFriendApplicationList(ctx, req)
+ if err != nil {
+ return err
+ }
+ localData, err := r.db.GetSendFriendApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return r.requestSendSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil, false, true)
+}
+
+// SyncAllFriendApplication recv
+func (r *Relation) SyncAllFriendApplication(ctx context.Context) error {
+ if !r.requestRecvSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestRecvSyncerLock.Unlock()
+ req := &relation.GetPaginationFriendsApplyToReq{UserID: r.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ requests, err := r.getFriendApplicationList(ctx, req)
+ if err != nil {
+ return err
+ }
+ localData, err := r.db.GetRecvFriendApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return r.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil)
+}
+func (r *Relation) SyncAllFriendApplicationWithoutNotice(ctx context.Context) error {
+ if !r.requestRecvSyncerLock.TryLock() {
+ return nil
+ }
+ defer r.requestRecvSyncerLock.Unlock()
+ req := &relation.GetPaginationFriendsApplyToReq{UserID: r.loginUserID, Pagination: &sdkws.RequestPagination{}}
+ requests, err := r.getFriendApplicationList(ctx, req)
+ if err != nil {
+ return err
+ }
+ localData, err := r.db.GetRecvFriendApplication(ctx)
+ if err != nil {
+ return err
+ }
+ return r.requestRecvSyncer.Sync(ctx, datautil.Batch(ServerFriendRequestToLocalFriendRequest, requests), localData, nil, false, true)
+}
+
+func (r *Relation) SyncAllFriendList(ctx context.Context) error {
+ t := time.Now()
+ defer func(start time.Time) {
+ elapsed := time.Since(start).Milliseconds()
+ log.ZDebug(ctx, "SyncAllFriendList fn call end", "cost time", fmt.Sprintf("%d ms", elapsed))
+
+ }(t)
+ return r.IncrSyncFriends(ctx)
+}
+
+func (r *Relation) SyncAllBlackList(ctx context.Context) error {
+ serverData, err := r.getBlackList(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "black from server", "data", serverData)
+ localData, err := r.db.GetBlackListDB(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "black from local", "data", localData)
+ return r.blackSyncer.Sync(ctx, datautil.Batch(ServerBlackToLocalBlack, serverData), localData, nil)
+}
+
+func (r *Relation) SyncAllBlackListWithoutNotice(ctx context.Context) error {
+ serverData, err := r.getBlackList(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "black from server", "data", serverData)
+ localData, err := r.db.GetBlackListDB(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "black from local", "data", localData)
+ return r.blackSyncer.Sync(ctx, datautil.Batch(ServerBlackToLocalBlack, serverData), localData, nil, false, true)
+}
+
+func (r *Relation) GetDesignatedFriends(ctx context.Context, friendIDs []string) ([]*sdkws.FriendInfo, error) {
+ return r.getDesignatedFriends(ctx, friendIDs)
+}
diff --git a/internal/third/api.go b/internal/third/api.go
new file mode 100644
index 000000000..6a84ee6cd
--- /dev/null
+++ b/internal/third/api.go
@@ -0,0 +1,33 @@
+package third
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+
+ "github.com/openimsdk/protocol/third"
+)
+
+func (c *Third) UpdateFcmToken(ctx context.Context, fcmToken string, expireTime int64) error {
+ return api.FcmUpdateToken.Execute(ctx, &third.FcmUpdateTokenReq{
+ PlatformID: c.platformID,
+ FcmToken: fcmToken,
+ Account: c.loginUserID,
+ ExpireTime: expireTime,
+ })
+}
+
+func (c *Third) SetAppBadge(ctx context.Context, appUnreadCount int32) error {
+ return api.SetAppBadge.Execute(ctx, &third.SetAppBadgeReq{
+ UserID: c.loginUserID,
+ AppUnreadCount: appUnreadCount,
+ })
+}
+
+func (c *Third) UploadLogs(ctx context.Context, line int, ex string, progress Progress) (err error) {
+ return c.uploadLogs(ctx, line, ex, progress)
+}
+
+func (c *Third) Log(ctx context.Context, logLevel int, file string, line int, msg, err string, keysAndValues []any) {
+ c.printLog(ctx, logLevel, file, line, msg, err, keysAndValues)
+}
diff --git a/internal/file/bitmap.go b/internal/third/file/bitmap.go
similarity index 100%
rename from internal/file/bitmap.go
rename to internal/third/file/bitmap.go
diff --git a/internal/file/cb.go b/internal/third/file/cb.go
similarity index 90%
rename from internal/file/cb.go
rename to internal/third/file/cb.go
index f7cb06232..6c17f0adf 100644
--- a/internal/file/cb.go
+++ b/internal/third/file/cb.go
@@ -17,14 +17,14 @@ package file
import "fmt"
type UploadFileCallback interface {
- Open(size int64) // 文件打开的大小
- PartSize(partSize int64, num int) // 分片大小,数量
- HashPartProgress(index int, size int64, partHash string) // 每块分片的hash值
- HashPartComplete(partsHash string, fileHash string) // 分块完成,服务端标记hash和文件最终hash
- UploadID(uploadID string) // 上传ID
- UploadPartComplete(index int, partSize int64, partHash string) // 上传分片进度
- UploadComplete(fileSize int64, streamSize int64, storageSize int64) // 整体进度
- Complete(size int64, url string, typ int) // 上传完成
+ Open(size int64) // file opening size
+ PartSize(partSize int64, num int) // shard size, number
+ HashPartProgress(index int, size int64, partHash string) // hash value of each shard
+ HashPartComplete(partsHash string, fileHash string) // sharding is complete, server marks hash and file final hash
+ UploadID(uploadID string) // upload ID
+ UploadPartComplete(index int, partSize int64, partHash string) // upload shard progress
+ UploadComplete(fileSize int64, streamSize int64, storageSize int64) // overall progress
+ Complete(size int64, url string, typ int) // upload completed
}
type emptyUploadCallback struct{}
diff --git a/internal/file/file.go b/internal/third/file/file.go
similarity index 100%
rename from internal/file/file.go
rename to internal/third/file/file.go
diff --git a/internal/file/file_default.go b/internal/third/file/file_default.go
similarity index 100%
rename from internal/file/file_default.go
rename to internal/third/file/file_default.go
diff --git a/internal/file/file_js.go b/internal/third/file/file_js.go
similarity index 100%
rename from internal/file/file_js.go
rename to internal/third/file/file_js.go
diff --git a/internal/file/file_test.go b/internal/third/file/file_test.go
similarity index 94%
rename from internal/file/file_test.go
rename to internal/third/file/file_test.go
index a10bbc64f..97f982926 100644
--- a/internal/file/file_test.go
+++ b/internal/third/file/file_test.go
@@ -19,7 +19,6 @@ func TestUpload(t *testing.T) {
ctx := ccontext.WithInfo(context.WithValue(context.Background(), "operationID", "OP123456"), conf)
f := NewFile(nil, conf.UserID)
- //fp := `C:\Users\openIM\Desktop\微信截图_20231025170714.png`
//fp := `C:\Users\openIM\Desktop\my_image (2).tar`
//fp := `C:\Users\openIM\Desktop\1234.zip`
//fp := `C:\Users\openIM\Desktop\openIM.wasm`
diff --git a/internal/file/md5.go b/internal/third/file/md5.go
similarity index 100%
rename from internal/file/md5.go
rename to internal/third/file/md5.go
diff --git a/internal/file/progress.go b/internal/third/file/progress.go
similarity index 100%
rename from internal/file/progress.go
rename to internal/third/file/progress.go
diff --git a/internal/file/upload.go b/internal/third/file/upload.go
similarity index 87%
rename from internal/file/upload.go
rename to internal/third/file/upload.go
index 4c9714e4d..598c7c233 100644
--- a/internal/file/upload.go
+++ b/internal/third/file/upload.go
@@ -21,8 +21,7 @@ import (
"encoding/hex"
"errors"
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/tools/errs"
@@ -148,7 +147,7 @@ func (f *File) UploadFile(ctx context.Context, req *UploadFileReq, cb UploadFile
Hash: partMd5Val,
Size: fileSize,
PartSize: partSize,
- MaxParts: int32(maxParts), // 一次性获取签名数量
+ MaxParts: int32(maxParts), // retrieve the number of signatures in one go
Cause: req.Cause,
Name: req.Name,
ContentType: req.ContentType,
@@ -242,18 +241,22 @@ func (f *File) cleanPartLimit() {
}
func (f *File) initiateMultipartUploadResp(ctx context.Context, req *third.InitiateMultipartUploadReq) (*third.InitiateMultipartUploadResp, error) {
- return util.CallApi[third.InitiateMultipartUploadResp](ctx, constant.ObjectInitiateMultipartUpload, req)
+ return api.ObjectInitiateMultipartUpload.Invoke(ctx, req)
}
func (f *File) authSign(ctx context.Context, req *third.AuthSignReq) (*third.AuthSignResp, error) {
if len(req.PartNumbers) == 0 {
return nil, errs.ErrArgs.WrapMsg("partNumbers is empty")
}
- return util.CallApi[third.AuthSignResp](ctx, constant.ObjectAuthSign, req)
+ return api.ObjectAuthSign.Invoke(ctx, req)
}
func (f *File) completeMultipartUpload(ctx context.Context, req *third.CompleteMultipartUploadReq) (*third.CompleteMultipartUploadResp, error) {
- return util.CallApi[third.CompleteMultipartUploadResp](ctx, constant.ObjectCompleteMultipartUpload, req)
+ return api.ObjectCompleteMultipartUpload.Invoke(ctx, req)
+}
+
+func (f *File) getObjectPartLimit(ctx context.Context) (*third.PartLimitResp, error) {
+ return api.ObjectPartLimit.Invoke(ctx, &third.PartLimitReq{})
}
func (f *File) getPartNum(fileSize int64, partSize int64) int {
@@ -268,11 +271,11 @@ func (f *File) partSize(ctx context.Context, size int64) (int64, error) {
f.confLock.Lock()
defer f.confLock.Unlock()
if f.partLimit == nil {
- resp, err := util.CallApi[third.PartLimitResp](ctx, constant.ObjectPartLimit, &third.PartLimitReq{})
+ var err error
+ f.partLimit, err = f.getObjectPartLimit(ctx)
if err != nil {
return 0, err
}
- f.partLimit = resp
}
if size <= 0 {
return 0, errors.New("size must be greater than 0")
@@ -291,7 +294,7 @@ func (f *File) partSize(ctx context.Context, size int64) (int64, error) {
}
func (f *File) accessURL(ctx context.Context, req *third.AccessURLReq) (*third.AccessURLResp, error) {
- return util.CallApi[third.AccessURLResp](ctx, constant.ObjectAccessURL, req)
+ return api.ObjectAccessURL.Invoke(ctx, req)
}
func (f *File) doHttpReq(req *http.Request) ([]byte, *http.Response, error) {
@@ -319,11 +322,10 @@ type AuthSignParts struct {
}
type UploadInfo struct {
- PartNum int
- Bitmap *Bitmap
- DBInfo *model_struct.LocalUpload
- Resp *third.InitiateMultipartUploadResp
- //Signs *AuthSignParts
+ PartNum int
+ Bitmap *Bitmap
+ DBInfo *model_struct.LocalUpload
+ Resp *third.InitiateMultipartUploadResp
CreateTime time.Time
BatchSignNum int32
f *File
@@ -417,45 +419,52 @@ func (u *UploadInfo) GetPartSign(ctx context.Context, partNumber int32) (*url.UR
return u.buildRequest(index)
}
-func (f *File) getUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*UploadInfo, error) {
+func (f *File) getLocalUploadInfo(ctx context.Context, req *third.InitiateMultipartUploadReq) (info *UploadInfo) {
partNum := f.getPartNum(req.Size, req.PartSize)
- var bitmap *Bitmap
- if f.database != nil {
- dbUpload, err := f.database.GetUpload(ctx, req.Hash)
- if err == nil {
- bitmapBytes, err := base64.StdEncoding.DecodeString(dbUpload.UploadInfo)
- if err != nil || len(bitmapBytes) == 0 || partNum <= 1 || dbUpload.ExpireTime-3600*1000 < time.Now().UnixMilli() {
- if err := f.database.DeleteUpload(ctx, req.Hash); err != nil {
- return nil, err
- }
- dbUpload = nil
- }
- if dbUpload == nil {
- bitmap = NewBitmap(partNum)
- } else {
- bitmap = ParseBitmap(bitmapBytes, partNum)
- }
- tUpInfo := &third.UploadInfo{
- PartSize: req.PartSize,
- Sign: &third.AuthSignParts{},
- }
- if dbUpload != nil {
- tUpInfo.UploadID = dbUpload.UploadID
- tUpInfo.ExpireTime = dbUpload.ExpireTime
+ if partNum <= 1 {
+ return nil
+ }
+ dbUpload, err := f.database.GetUpload(ctx, req.Hash)
+ if err != nil {
+ return nil
+ }
+ defer func() {
+ if info == nil {
+ if err := f.database.DeleteUpload(ctx, req.Hash); err != nil {
+ log.ZError(ctx, "delete upload db", err, "partHash", req.Hash)
}
- return &UploadInfo{
- PartNum: partNum,
- Bitmap: bitmap,
- DBInfo: dbUpload,
- Resp: &third.InitiateMultipartUploadResp{
- Upload: tUpInfo,
- },
- BatchSignNum: req.MaxParts,
- f: f,
- }, nil
}
- log.ZError(ctx, "get upload db", err, "pratsMd5", req.Hash)
+ }()
+ if dbUpload.UploadID == "" || dbUpload.ExpireTime-3600*1000 < time.Now().UnixMilli() {
+ return nil
}
+ bitmapBytes, err := base64.StdEncoding.DecodeString(dbUpload.UploadInfo)
+ if err != nil {
+ log.ZError(ctx, "decode upload info", err, "partHash", req.Hash)
+ return nil
+ }
+ return &UploadInfo{
+ PartNum: partNum,
+ Bitmap: ParseBitmap(bitmapBytes, partNum),
+ DBInfo: dbUpload,
+ Resp: &third.InitiateMultipartUploadResp{
+ Upload: &third.UploadInfo{
+ PartSize: req.PartSize,
+ Sign: &third.AuthSignParts{},
+ UploadID: dbUpload.UploadID,
+ ExpireTime: dbUpload.ExpireTime,
+ },
+ },
+ BatchSignNum: req.MaxParts,
+ f: f,
+ }
+}
+
+func (f *File) getUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*UploadInfo, error) {
+ if info := f.getLocalUploadInfo(ctx, req); info != nil {
+ return info, nil
+ }
+ partNum := f.getPartNum(req.Size, req.PartSize)
resp, err := f.initiateMultipartUploadResp(ctx, req)
if err != nil {
return nil, err
@@ -465,9 +474,9 @@ func (f *File) getUpload(ctx context.Context, req *third.InitiateMultipartUpload
Resp: resp,
}, nil
}
- bitmap = NewBitmap(partNum)
+ bitmap := NewBitmap(partNum)
var dbUpload *model_struct.LocalUpload
- if f.database != nil {
+ if partNum > 1 {
dbUpload = &model_struct.LocalUpload{
PartHash: req.Hash,
UploadID: resp.Upload.UploadID,
@@ -475,13 +484,13 @@ func (f *File) getUpload(ctx context.Context, req *third.InitiateMultipartUpload
ExpireTime: resp.Upload.ExpireTime,
CreateTime: time.Now().UnixMilli(),
}
+ if err := f.database.DeleteUpload(ctx, req.Hash); err != nil {
+ log.ZError(ctx, "delete upload db", err, "partHash", req.Hash)
+ }
if err := f.database.InsertUpload(ctx, dbUpload); err != nil {
log.ZError(ctx, "insert upload db", err, "pratsHash", req.Hash, "name", req.Name)
}
}
- if req.MaxParts >= 0 && len(resp.Upload.Sign.Parts) != int(req.MaxParts) {
- resp.Upload.Sign.Parts = nil
- }
return &UploadInfo{
PartNum: partNum,
Bitmap: bitmap,
diff --git a/internal/third/log.go b/internal/third/log.go
index aff0b6e82..1d7da634e 100644
--- a/internal/third/log.go
+++ b/internal/third/log.go
@@ -1,56 +1,126 @@
package third
import (
+ "bufio"
"context"
- "errors"
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/protocol/third"
"io"
"math/rand"
"os"
"path/filepath"
"strings"
"time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ "github.com/openimsdk/openim-sdk-core/v3/version"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
)
-func (c *Third) UploadLogs(ctx context.Context, progress Progress) error {
+const (
+ buffer = 10 * 1024 * 1024
+)
+
+func (c *Third) uploadLogs(ctx context.Context, line int, ex string, progress Progress) (err error) {
+ if c.logUploadLock.TryLock() {
+ defer c.logUploadLock.Unlock()
+ } else {
+ return errs.New("log file is uploading").Wrap()
+ }
+ if line < 0 {
+ return errs.New("line is illegal").Wrap()
+ }
+
logFilePath := c.LogFilePath
entrys, err := os.ReadDir(logFilePath)
if err != nil {
return err
}
files := make([]string, 0, len(entrys))
- for _, entry := range entrys {
- if (!entry.IsDir()) && (!strings.HasSuffix(entry.Name(), ".zip")) && checkLogPath(entry.Name()) {
- files = append(files, filepath.Join(logFilePath, entry.Name()))
+ switch line {
+ case 0:
+ // all logs
+ for _, entry := range entrys {
+ if (!entry.IsDir()) && (!strings.HasSuffix(entry.Name(), ".zip")) && checkLogPath(entry.Name()) {
+ files = append(files, filepath.Join(logFilePath, entry.Name()))
+ }
}
+ if len(files) == 0 {
+ return errs.New("not found log file").Wrap()
+ }
+ defer func() {
+ if err == nil {
+ // remove old file
+ for _, f := range files[:len(files)-1] {
+ if err := os.Remove(f); err != nil {
+ log.ZError(ctx, "remove file failed", err, "file name", f)
+ }
+ }
+ // truncate now log file
+ f, err := os.OpenFile(files[len(files)-1], os.O_WRONLY|os.O_TRUNC, 0644)
+ if err != nil {
+ log.ZError(ctx, "remove file failed", err, "file name", f)
+ }
+ _ = f.Close()
+ }
+ }()
+ default:
+ for i := len(entrys) - 1; i >= 0; i-- {
+ // get newest log file
+ if (!entrys[i].IsDir()) && (!strings.HasSuffix(entrys[i].Name(), ".zip")) && checkLogPath(entrys[i].Name()) {
+ files = append(files, filepath.Join(logFilePath, entrys[i].Name()))
+ break
+ }
+ }
+ if len(files) == 0 {
+ return errs.New("not found log file").Wrap()
+ }
+ lines, err := readLastNLines(files[0], line)
+ if err != nil {
+ return err
+ }
+ data := strings.Join(lines, "\n")
+
+ // create tmp file
+ filename := fmt.Sprintf("%s_temp%s", strings.TrimSuffix(filepath.Base(files[0]), filepath.Ext(files[0])), filepath.Ext(files[0]))
+ files[0] = filepath.Join(logFilePath, filename)
+ err = os.WriteFile(files[0], []byte(data), 0644)
+ if err != nil {
+ return errs.Wrap(err)
+ }
+ defer func() {
+ if err := os.Remove(files[0]); err != nil {
+ log.ZError(ctx, "remove file failed", err, "file name", files[0])
+ }
+ }()
}
- if len(files) == 0 {
- return errors.New("not found log file")
- }
+
zippath := filepath.Join(logFilePath, fmt.Sprintf("%d_%d.zip", time.Now().UnixMilli(), rand.Uint32()))
defer os.Remove(zippath)
if err := zipFiles(zippath, files); err != nil {
return err
}
- reqUpload := &file.UploadFileReq{Filepath: zippath, Name: fmt.Sprintf("sdk_log_%s_%s", c.loginUserID, filepath.Base(zippath)), Cause: "sdklog", ContentType: "application/zip"}
+ reqUpload := &file.UploadFileReq{Filepath: zippath, Name: fmt.Sprintf("sdk_log_%s_%s_%s_%s_%s",
+ c.loginUserID, c.systemType, constant.PlatformID2Name[int(c.platformID)], version.Version, filepath.Base(zippath)), Cause: "sdklog", ContentType: "application/zip"}
resp, err := c.fileUploader.UploadFile(ctx, reqUpload, &progressConvert{ctx: ctx, p: progress})
if err != nil {
return err
}
ccontext.Info(ctx)
reqLog := &third.UploadLogsReq{
- Platform: c.platformID,
- SystemType: c.systemType,
- Version: c.version,
- FileURLs: []*third.FileURL{{Filename: zippath, URL: resp.URL}},
+ Platform: c.platformID,
+ AppFramework: c.systemType,
+ Version: version.Version,
+ FileURLs: []*third.FileURL{{Filename: zippath, URL: resp.URL}},
+ Ex: ex,
}
- _, err = util.CallApi[third.UploadLogsResp](ctx, constant.UploadLogsRouter, reqLog)
- return err
+ return api.UploadLogs.Execute(ctx, reqLog)
}
func checkLogPath(logPath string) bool {
@@ -83,3 +153,44 @@ func (c *Third) fileCopy(src, dst string) error {
_, err = io.Copy(dstFile, srcFile)
return err
}
+
+func readLastNLines(filename string, n int) ([]string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ lines := make([]string, n)
+ count := 0
+
+ scanner := bufio.NewScanner(f)
+ buf := make([]byte, buffer)
+ scanner.Buffer(buf, buffer)
+
+ for scanner.Scan() {
+ lines[count%n] = scanner.Text()
+ count++
+ }
+
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+
+ start := count - n
+ if start < 0 {
+ start = 0
+ }
+ result := make([]string, 0, n)
+ for i := start; i < count; i++ {
+ result = append(result, lines[i%n])
+ }
+
+ return result, nil
+}
+
+func (c *Third) printLog(ctx context.Context, logLevel int, file string, line int, msg, err string, keysAndValues []any) {
+ errString := errs.New(err)
+
+ log.SDKLog(ctx, logLevel, file, line, msg, errString, keysAndValues)
+}
diff --git a/internal/third/third.go b/internal/third/third.go
index a7bf83545..535dd0374 100644
--- a/internal/third/third.go
+++ b/internal/third/third.go
@@ -15,43 +15,19 @@
package third
import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/protocol/third"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
+ "sync"
)
type Third struct {
- platformID int32
- loginUserID string
- version string
- systemType string
- LogFilePath string
- fileUploader *file.File
-}
-
-func NewThird(platformID int32, loginUserID, version, systemType, LogFilePath string, fileUploader *file.File) *Third {
- return &Third{platformID: platformID, loginUserID: loginUserID, version: version, systemType: systemType, LogFilePath: LogFilePath, fileUploader: fileUploader}
-}
-
-func (c *Third) UpdateFcmToken(ctx context.Context, fcmToken string, expireTime int64) error {
- req := third.FcmUpdateTokenReq{
- PlatformID: c.platformID,
- FcmToken: fcmToken,
- Account: c.loginUserID,
- ExpireTime: expireTime}
- _, err := util.CallApi[third.FcmUpdateTokenResp](ctx, constant.FcmUpdateTokenRouter, &req)
- return err
-
+ platformID int32
+ loginUserID string
+ systemType string
+ LogFilePath string
+ fileUploader *file.File
+ logUploadLock sync.Mutex
}
-func (c *Third) SetAppBadge(ctx context.Context, appUnreadCount int32) error {
- req := third.SetAppBadgeReq{
- UserID: c.loginUserID,
- AppUnreadCount: appUnreadCount,
- }
- _, err := util.CallApi[third.SetAppBadgeResp](ctx, constant.SetAppBadgeRouter, &req)
- return err
+func NewThird(platformID int32, loginUserID, systemType, LogFilePath string, fileUploader *file.File) *Third {
+ return &Third{platformID: platformID, loginUserID: loginUserID, systemType: systemType, LogFilePath: LogFilePath, fileUploader: fileUploader}
}
diff --git a/internal/user/api.go b/internal/user/api.go
new file mode 100644
index 000000000..6ccaa4fbd
--- /dev/null
+++ b/internal/user/api.go
@@ -0,0 +1,130 @@
+package user
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/common"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/sdkws"
+ userPb "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+// ProcessUserCommandGetAll get user's choice
+func (u *User) ProcessUserCommandGetAll(ctx context.Context) ([]*userPb.CommandInfoResp, error) {
+ localCommands, err := u.DataBase.ProcessUserCommandGetAll(ctx)
+ if err != nil {
+ return nil, err // Handle the error appropriately
+ }
+ var result []*userPb.CommandInfoResp
+ for _, localCommand := range localCommands {
+ result = append(result, &userPb.CommandInfoResp{
+ Type: localCommand.Type,
+ CreateTime: localCommand.CreateTime,
+ Uuid: localCommand.Uuid,
+ Value: localCommand.Value,
+ })
+ }
+ return result, nil
+}
+
+func (u *User) UserOnlineStatusChange(users map[string][]int32) {
+ for userID, onlinePlatformIDs := range users {
+ status := userPb.OnlineStatus{
+ UserID: userID,
+ PlatformIDs: onlinePlatformIDs,
+ }
+ if len(status.PlatformIDs) == 0 {
+ status.Status = constant.Offline
+ } else {
+ status.Status = constant.Online
+ }
+ u.listener().OnUserStatusChanged(utils.StructToJsonString(&status))
+ }
+}
+
+func (u *User) GetSelfUserInfo(ctx context.Context) (*model_struct.LocalUser, error) {
+ return u.GetUserInfoWithCache(ctx, u.loginUserID)
+}
+
+func (u *User) SetSelfInfo(ctx context.Context, userInfo *sdkws.UserInfoWithEx) error {
+ // updateSelfUserInfo updates the user's information with Ex field.
+ userInfo.UserID = u.loginUserID
+ if err := u.updateUserInfo(ctx, userInfo); err != nil {
+ return err
+ }
+ err := u.SyncLoginUserInfo(ctx)
+ if err != nil {
+ log.ZWarn(ctx, "SyncLoginUserInfo", err)
+ }
+ return nil
+}
+
+// ProcessUserCommandAdd CRUD user command
+func (u *User) ProcessUserCommandAdd(ctx context.Context, userCommand *userPb.ProcessUserCommandAddReq) error {
+ req := &userPb.ProcessUserCommandAddReq{UserID: u.loginUserID, Type: userCommand.Type, Uuid: userCommand.Uuid, Value: userCommand.Value}
+ if err := u.processUserCommandAdd(ctx, req); err != nil {
+ return err
+ }
+ return u.SyncAllCommand(ctx)
+}
+
+// ProcessUserCommandDelete delete user's choice
+func (u *User) ProcessUserCommandDelete(ctx context.Context, userCommand *userPb.ProcessUserCommandDeleteReq) error {
+ req := &userPb.ProcessUserCommandDeleteReq{UserID: u.loginUserID, Type: userCommand.Type, Uuid: userCommand.Uuid}
+ if err := u.processUserCommandDelete(ctx, req); err != nil {
+ return err
+ }
+ return u.SyncAllCommand(ctx)
+}
+
+// ProcessUserCommandUpdate update user's choice
+func (u *User) ProcessUserCommandUpdate(ctx context.Context, userCommand *userPb.ProcessUserCommandUpdateReq) error {
+ req := &userPb.ProcessUserCommandUpdateReq{UserID: u.loginUserID, Type: userCommand.Type, Uuid: userCommand.Uuid, Value: userCommand.Value}
+ if err := u.processUserCommandUpdate(ctx, req); err != nil {
+ return err
+ }
+ return u.SyncAllCommand(ctx)
+}
+
+func (u *User) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdk_struct.PublicUser, error) {
+ usersInfo, err := u.GetUsersInfoWithCache(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+
+ res := datautil.Batch(LocalUserToPublicUser, usersInfo)
+
+ friendList, err := u.GetFriendInfoList(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+ friendMap := datautil.SliceToMap(friendList, func(friend *model_struct.LocalFriend) string {
+ return friend.FriendUserID
+ })
+
+ for _, userInfo := range res {
+
+ // update single conversation
+
+ conversation, err := u.GetConversationByUserID(ctx, userInfo.UserID)
+ if err != nil {
+ log.ZWarn(ctx, "GetConversationByUserID failed", err, "userInfo", usersInfo)
+ } else {
+ if _, ok := friendMap[userInfo.UserID]; ok {
+ continue
+ }
+ log.ZDebug(ctx, "GetConversationByUserID", "conversation", conversation)
+ if conversation.ShowName != userInfo.Nickname || conversation.FaceURL != userInfo.FaceURL {
+ _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.UpdateConFaceUrlAndNickName,
+ Args: common.SourceIDAndSessionType{SourceID: userInfo.UserID, SessionType: conversation.ConversationType, FaceURL: userInfo.FaceURL, Nickname: userInfo.Nickname}}, u.conversationCh)
+ _ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{Action: constant.UpdateMsgFaceUrlAndNickName,
+ Args: common.UpdateMessageInfo{SessionType: conversation.ConversationType, UserID: userInfo.UserID, FaceURL: userInfo.FaceURL, Nickname: userInfo.Nickname}}, u.conversationCh)
+ }
+ }
+ }
+ return res, nil
+}
diff --git a/internal/user/convert.go b/internal/user/conversion.go
similarity index 56%
rename from internal/user/convert.go
rename to internal/user/conversion.go
index 2f68844a3..2c13f6737 100644
--- a/internal/user/convert.go
+++ b/internal/user/conversion.go
@@ -1,21 +1,8 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
package user
import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/openimsdk/protocol/user"
"github.com/openimsdk/protocol/sdkws"
@@ -41,3 +28,12 @@ func ServerCommandToLocalCommand(data *user.AllCommandInfoResp) *model_struct.Lo
Value: data.Value,
}
}
+func LocalUserToPublicUser(user *model_struct.LocalUser) *sdk_struct.PublicUser {
+ return &sdk_struct.PublicUser{
+ UserID: user.UserID,
+ Nickname: user.Nickname,
+ FaceURL: user.FaceURL,
+ Ex: user.Ex,
+ CreateTime: user.CreateTime,
+ }
+}
diff --git a/internal/user/full_sync.go b/internal/user/full_sync.go
new file mode 100644
index 000000000..e4fbe5d37
--- /dev/null
+++ b/internal/user/full_sync.go
@@ -0,0 +1,71 @@
+package user
+
+import (
+ "context"
+ "errors"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ userPb "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+func (u *User) SyncLoginUserInfo(ctx context.Context) error {
+ remoteUser, err := u.GetSingleUserFromServer(ctx, u.loginUserID)
+ if err != nil {
+ return err
+ }
+ localUser, err := u.GetLoginUser(ctx, u.loginUserID)
+ if err != nil && errors.Is(errs.Unwrap(err), errs.ErrRecordNotFound) {
+ return err
+ }
+ var localUsers []*model_struct.LocalUser
+ if err == nil {
+ localUsers = []*model_struct.LocalUser{localUser}
+ }
+ log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser)
+ return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil)
+}
+
+func (u *User) SyncLoginUserInfoWithoutNotice(ctx context.Context) error {
+ remoteUser, err := u.GetSingleUserFromServer(ctx, u.loginUserID)
+ if err != nil {
+ return err
+ }
+ localUser, err := u.GetLoginUser(ctx, u.loginUserID)
+ if err != nil && errors.Is(errs.Unwrap(err), errs.ErrRecordNotFound) {
+ log.ZError(ctx, "SyncLoginUserInfo", err)
+ }
+ var localUsers []*model_struct.LocalUser
+ if err == nil {
+ localUsers = []*model_struct.LocalUser{localUser}
+ }
+ log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser)
+ return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil, false, true)
+}
+
+func (u *User) SyncAllCommand(ctx context.Context) error {
+ return u.syncAllCommand(ctx, true)
+}
+
+func (u *User) SyncAllCommandWithoutNotice(ctx context.Context) error {
+ return u.syncAllCommand(ctx, false)
+}
+
+func (u *User) syncAllCommand(ctx context.Context, withNotice bool) error {
+ resp, err := u.processUserCommandGetAll(ctx, &userPb.ProcessUserCommandGetAllReq{UserID: u.loginUserID})
+ if err != nil {
+ return err
+ }
+ localData, err := u.DataBase.ProcessUserCommandGetAll(ctx)
+ if err != nil {
+ return err
+ }
+ log.ZDebug(ctx, "sync command", "data from server", resp, "data from local", localData)
+ if withNotice {
+ return u.commandSyncer.Sync(ctx, datautil.Batch(ServerCommandToLocalCommand, resp.CommandResp), localData, nil)
+ } else {
+ return u.commandSyncer.Sync(ctx, datautil.Batch(ServerCommandToLocalCommand, resp.CommandResp), localData, nil, false, true)
+ }
+}
diff --git a/internal/user/notification.go b/internal/user/notification.go
new file mode 100644
index 000000000..e43c73923
--- /dev/null
+++ b/internal/user/notification.go
@@ -0,0 +1,95 @@
+package user
+
+import (
+ "context"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+)
+
+// DoNotification handles incoming notifications for the user.
+func (u *User) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
+ log.ZDebug(ctx, "user notification", "msg", msg)
+ go func() {
+ if err := u.doNotification(ctx, msg); err != nil {
+ log.ZError(ctx, "DoUserNotification failed", err)
+ }
+ }()
+}
+
+func (u *User) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ switch msg.ContentType {
+ case constant.UserInfoUpdatedNotification:
+ return u.userInfoUpdatedNotification(ctx, msg)
+ case constant.UserCommandAddNotification:
+ return u.userCommandAddNotification(ctx, msg)
+ case constant.UserCommandDeleteNotification:
+ return u.userCommandDeleteNotification(ctx, msg)
+ case constant.UserCommandUpdateNotification:
+ return u.userCommandUpdateNotification(ctx, msg)
+ default:
+ return errs.New("unknown content type", "contentType", msg.ContentType, "clientMsgID", msg.ClientMsgID, "serverMsgID", msg.ServerMsgID).Wrap()
+ }
+}
+
+// userInfoUpdatedNotification handles notifications about updated user information.
+func (u *User) userInfoUpdatedNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ tips := sdkws.UserInfoUpdatedTips{}
+ if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
+ return err
+ }
+
+ if tips.UserID == u.loginUserID {
+ err := u.SyncLoginUserInfo(ctx)
+ if err != nil {
+ return err
+ }
+ } else {
+ log.ZDebug(ctx, "detail.UserID != u.loginUserID, do nothing", "detail.UserID", tips.UserID, "u.loginUserID", u.loginUserID)
+ }
+ return nil
+}
+
+// userCommandAddNotification handle notification when user add favorite
+func (u *User) userCommandAddNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ tip := sdkws.UserCommandAddTips{}
+ if tip.ToUserID == u.loginUserID {
+ err := u.SyncAllCommand(ctx)
+ if err != nil {
+ return err
+ }
+ } else {
+ log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
+ }
+ return nil
+}
+
+// userCommandDeleteNotification handle notification when user delete favorite
+func (u *User) userCommandDeleteNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ tip := sdkws.UserCommandDeleteTips{}
+ if tip.ToUserID == u.loginUserID {
+ err := u.SyncAllCommand(ctx)
+ if err != nil {
+ return err
+ }
+ } else {
+ log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
+ }
+ return nil
+}
+
+// userCommandUpdateNotification handle notification when user update favorite
+func (u *User) userCommandUpdateNotification(ctx context.Context, msg *sdkws.MsgData) error {
+ tip := sdkws.UserCommandUpdateTips{}
+ if tip.ToUserID == u.loginUserID {
+ err := u.SyncAllCommand(ctx)
+ if err != nil {
+ return err
+ }
+ } else {
+ log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
+ }
+ return nil
+}
diff --git a/internal/user/sdk.go b/internal/user/sdk.go
deleted file mode 100644
index 6a97c913a..000000000
--- a/internal/user/sdk.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package user
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- pbUser "github.com/openimsdk/protocol/user"
- userPb "github.com/openimsdk/protocol/user"
-
- "github.com/openimsdk/protocol/sdkws"
-)
-
-func (u *User) GetUsersInfo(ctx context.Context, userIDs []string) ([]*model_struct.LocalUser, error) {
- return u.GetUsersInfoFromSvr(ctx, userIDs)
-}
-
-func (u *User) GetSelfUserInfo(ctx context.Context) (*model_struct.LocalUser, error) {
- return u.getSelfUserInfo(ctx)
-}
-
-// Deprecated: user SetSelfInfoEx instead
-func (u *User) SetSelfInfo(ctx context.Context, userInfo *sdkws.UserInfo) error {
- return u.updateSelfUserInfo(ctx, userInfo)
-}
-func (u *User) SetSelfInfoEx(ctx context.Context, userInfo *sdkws.UserInfoWithEx) error {
- return u.updateSelfUserInfoEx(ctx, userInfo)
-}
-func (u *User) SetGlobalRecvMessageOpt(ctx context.Context, opt int) error {
- if err := util.ApiPost(ctx, constant.SetGlobalRecvMessageOptRouter,
- &pbUser.SetGlobalRecvMessageOptReq{UserID: u.loginUserID, GlobalRecvMsgOpt: int32(opt)}, nil); err != nil {
- return err
- }
- u.SyncLoginUserInfo(ctx)
- return nil
-}
-
-func (u *User) UpdateMsgSenderInfo(ctx context.Context, nickname, faceURL string) (err error) {
- if nickname != "" {
- if err = u.DataBase.UpdateMsgSenderNickname(ctx, u.loginUserID, nickname, constant.SingleChatType); err != nil {
- return err
- }
- }
- if faceURL != "" {
- if err = u.DataBase.UpdateMsgSenderFaceURL(ctx, u.loginUserID, faceURL, constant.SingleChatType); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (u *User) SubscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
- userStatus, err := u.subscribeUsersStatus(ctx, userIDs)
- if err != nil {
- return nil, err
- }
- u.OnlineStatusCache.DeleteAll()
- u.OnlineStatusCache.StoreAll(func(value *userPb.OnlineStatus) string {
- return value.UserID
- }, userStatus)
- return userStatus, nil
-}
-
-func (u *User) UnsubscribeUsersStatus(ctx context.Context, userIDs []string) error {
- u.OnlineStatusCache.DeleteAll()
- return u.unsubscribeUsersStatus(ctx, userIDs)
-}
-
-func (u *User) GetSubscribeUsersStatus(ctx context.Context) ([]*userPb.OnlineStatus, error) {
- return u.getSubscribeUsersStatus(ctx)
-}
-
-func (u *User) GetUserStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
- return u.getUserStatus(ctx, userIDs)
-}
diff --git a/internal/user/server_api.go b/internal/user/server_api.go
new file mode 100644
index 000000000..5c3f8ce73
--- /dev/null
+++ b/internal/user/server_api.go
@@ -0,0 +1,35 @@
+package user
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/protocol/user"
+)
+
+func (u *User) getUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
+ req := &user.GetDesignateUsersReq{UserIDs: userIDs}
+ return api.ExtractField(ctx, api.GetUsersInfo.Invoke, req, (*user.GetDesignateUsersResp).GetUsersInfo)
+}
+
+func (u *User) updateUserInfo(ctx context.Context, userInfo *sdkws.UserInfoWithEx) error {
+ userInfo.UserID = u.loginUserID
+ return api.UpdateUserInfoEx.Execute(ctx, &user.UpdateUserInfoExReq{UserInfo: userInfo})
+}
+
+func (u *User) processUserCommandAdd(ctx context.Context, req *user.ProcessUserCommandAddReq) error {
+ return api.ProcessUserCommandAdd.Execute(ctx, req)
+}
+
+func (u *User) processUserCommandDelete(ctx context.Context, req *user.ProcessUserCommandDeleteReq) error {
+ return api.ProcessUserCommandDelete.Execute(ctx, req)
+}
+
+func (u *User) processUserCommandUpdate(ctx context.Context, req *user.ProcessUserCommandUpdateReq) error {
+ return api.ProcessUserCommandUpdate.Execute(ctx, req)
+}
+
+func (u *User) processUserCommandGetAll(ctx context.Context, req *user.ProcessUserCommandGetAllReq) (*user.ProcessUserCommandGetAllResp, error) {
+ return api.ProcessUserCommandGetAll.Invoke(ctx, req)
+}
diff --git a/internal/user/sync.go b/internal/user/sync.go
deleted file mode 100644
index 2d2d60af3..000000000
--- a/internal/user/sync.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package user
-
-import (
- "context"
- "errors"
-
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- userPb "github.com/openimsdk/protocol/user"
- "github.com/openimsdk/tools/errs"
- "github.com/openimsdk/tools/log"
- "github.com/openimsdk/tools/utils/datautil"
-)
-
-func (u *User) SyncLoginUserInfo(ctx context.Context) error {
- remoteUser, err := u.GetSingleUserFromSvr(ctx, u.loginUserID)
- if err != nil {
- return err
- }
- localUser, err := u.GetLoginUser(ctx, u.loginUserID)
- if err != nil && errs.Unwrap(err) != errs.ErrRecordNotFound {
- log.ZError(ctx, "SyncLoginUserInfo", err)
- }
- var localUsers []*model_struct.LocalUser
- if err == nil {
- localUsers = []*model_struct.LocalUser{localUser}
- }
- log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser)
- return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil)
-}
-func (u *User) SyncLoginUserInfoWithoutNotice(ctx context.Context) error {
- remoteUser, err := u.GetSingleUserFromSvr(ctx, u.loginUserID)
- if err != nil {
- return err
- }
- localUser, err := u.GetLoginUser(ctx, u.loginUserID)
- if err != nil && errs.Unwrap(err) != errs.ErrRecordNotFound {
- log.ZError(ctx, "SyncLoginUserInfo", err)
- }
- var localUsers []*model_struct.LocalUser
- if err == nil {
- localUsers = []*model_struct.LocalUser{localUser}
- }
- log.ZDebug(ctx, "SyncLoginUserInfo", "remoteUser", remoteUser, "localUser", localUser)
- return u.userSyncer.Sync(ctx, []*model_struct.LocalUser{remoteUser}, localUsers, nil, false, true)
-}
-
-func (u *User) SyncUserStatus(ctx context.Context, fromUserID string, status int32, platformID int32) {
- userOnlineStatus := userPb.OnlineStatus{
- UserID: fromUserID,
- Status: status,
- PlatformIDs: []int32{platformID},
- }
- if v, ok := u.OnlineStatusCache.Load(fromUserID); ok {
- if status == constant.Online {
- v.PlatformIDs = utils.RemoveRepeatedElementsInList(append(v.PlatformIDs, platformID))
- u.OnlineStatusCache.Store(fromUserID, v)
- } else {
- v.PlatformIDs = utils.RemoveOneInList(v.PlatformIDs, platformID)
- if len(v.PlatformIDs) == 0 {
- v.Status = constant.Offline
- v.PlatformIDs = []int32{}
- u.OnlineStatusCache.Delete(fromUserID)
- }
- }
- u.listener().OnUserStatusChanged(utils.StructToJsonString(v))
- } else {
- if status == constant.Online {
- u.OnlineStatusCache.Store(fromUserID, &userOnlineStatus)
- u.listener().OnUserStatusChanged(utils.StructToJsonString(userOnlineStatus))
- } else {
- log.ZWarn(ctx, "exception", errors.New("user not exist"), "fromUserID", fromUserID,
- "status", status, "platformID", platformID)
- }
- }
-}
-
-type CommandInfoResponse struct {
- CommandResp []*userPb.AllCommandInfoResp `json:"CommandResp"`
-}
-
-func (u *User) SyncAllCommand(ctx context.Context) error {
- var serverData CommandInfoResponse
- err := util.ApiPost(ctx, constant.ProcessUserCommandGetAll, userPb.ProcessUserCommandGetAllReq{
- UserID: u.loginUserID,
- }, &serverData)
- if err != nil {
- return err
- }
- localData, err := u.DataBase.ProcessUserCommandGetAll(ctx)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "sync command", "data from server", serverData, "data from local", localData)
- return u.commandSyncer.Sync(ctx, datautil.Batch(ServerCommandToLocalCommand, serverData.CommandResp), localData, nil)
-}
-
-func (u *User) SyncAllCommandWithoutNotice(ctx context.Context) error {
- var serverData CommandInfoResponse
- err := util.ApiPost(ctx, constant.ProcessUserCommandGetAll, userPb.ProcessUserCommandGetAllReq{
- UserID: u.loginUserID,
- }, &serverData)
- if err != nil {
- return err
- }
- localData, err := u.DataBase.ProcessUserCommandGetAll(ctx)
- if err != nil {
- return err
- }
- log.ZDebug(ctx, "sync command", "data from server", serverData, "data from local", localData)
- return u.commandSyncer.Sync(ctx, datautil.Batch(ServerCommandToLocalCommand, serverData.CommandResp), localData, nil, false, true)
-}
diff --git a/internal/user/user.go b/internal/user/user.go
index 181930984..3216078d6 100644
--- a/internal/user/user.go
+++ b/internal/user/user.go
@@ -17,40 +17,46 @@ package user
import (
"context"
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/internal/cache"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
- authPb "github.com/openimsdk/protocol/auth"
- "github.com/openimsdk/protocol/sdkws"
- userPb "github.com/openimsdk/protocol/user"
"github.com/openimsdk/tools/log"
- "github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/cache"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- PbConstant "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/utils/datautil"
)
-type BasicInfo struct {
- Nickname string
- FaceURL string
+// NewUser creates a new User object.
+func NewUser(dataBase db_interface.DataBase, loginUserID string, conversationCh chan common.Cmd2Value) *User {
+ user := &User{DataBase: dataBase, loginUserID: loginUserID, conversationCh: conversationCh}
+ user.initSyncer()
+ //user.OnlineStatusCache = cache.NewCache[string, *userPb.OnlineStatus]()
+ user.UserCache = cache.NewUserCache[string, *model_struct.LocalUser](
+ func(value *model_struct.LocalUser) string { return value.UserID },
+ nil,
+ user.GetLoginUser,
+ user.GetUsersInfoFromServer,
+ )
+ return user
}
// User is a struct that represents a user in the system.
type User struct {
db_interface.DataBase
- loginUserID string
- listener func() open_im_sdk_callback.OnUserListener
- userSyncer *syncer.Syncer[*model_struct.LocalUser, syncer.NoResp, string]
- commandSyncer *syncer.Syncer[*model_struct.LocalUserCommand, syncer.NoResp, string]
- conversationCh chan common.Cmd2Value
- UserBasicCache *cache.Cache[string, *BasicInfo]
- OnlineStatusCache *cache.Cache[string, *userPb.OnlineStatus]
+ loginUserID string
+ listener func() open_im_sdk_callback.OnUserListener
+ userSyncer *syncer.Syncer[*model_struct.LocalUser, syncer.NoResp, string]
+ commandSyncer *syncer.Syncer[*model_struct.LocalUserCommand, syncer.NoResp, string]
+ conversationCh chan common.Cmd2Value
+ UserCache *cache.UserCache[string, *model_struct.LocalUser]
+
+ //OnlineStatusCache *cache.Cache[string, *userPb.OnlineStatus]
}
// SetListener sets the user's listener.
@@ -58,15 +64,6 @@ func (u *User) SetListener(listener func() open_im_sdk_callback.OnUserListener)
u.listener = listener
}
-// NewUser creates a new User object.
-func NewUser(dataBase db_interface.DataBase, loginUserID string, conversationCh chan common.Cmd2Value) *User {
- user := &User{DataBase: dataBase, loginUserID: loginUserID, conversationCh: conversationCh}
- user.initSyncer()
- user.UserBasicCache = cache.NewCache[string, *BasicInfo]()
- user.OnlineStatusCache = cache.NewCache[string, *userPb.OnlineStatus]()
- return user
-}
-
func (u *User) initSyncer() {
u.userSyncer = syncer.New[*model_struct.LocalUser, syncer.NoResp, string](
func(ctx context.Context, value *model_struct.LocalUser) error {
@@ -76,6 +73,7 @@ func (u *User) initSyncer() {
return fmt.Errorf("not support delete user %s", value.UserID)
},
func(ctx context.Context, serverUser, localUser *model_struct.LocalUser) error {
+ u.UserCache.Delete(localUser.UserID)
return u.DataBase.UpdateLoginUser(context.Background(), serverUser)
},
func(user *model_struct.LocalUser) string {
@@ -87,6 +85,8 @@ func (u *User) initSyncer() {
case syncer.Update:
u.listener().OnSelfInfoUpdated(utils.StructToJsonString(server))
if server.Nickname != local.Nickname || server.FaceURL != local.FaceURL {
+ _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.UpdateConFaceUrlAndNickName,
+ Args: common.SourceIDAndSessionType{SourceID: server.UserID, SessionType: constant.SingleChatType, FaceURL: server.FaceURL, Nickname: server.Nickname}}, u.conversationCh)
_ = common.TriggerCmdUpdateMessage(ctx, common.UpdateMessageNode{Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{SessionType: constant.SingleChatType, UserID: server.UserID, FaceURL: server.FaceURL, Nickname: server.Nickname}}, u.conversationCh)
}
@@ -141,287 +141,43 @@ func (u *User) initSyncer() {
)
}
-//func (u *User) equal(a, b *model_struct.LocalUser) bool {
-// if a.CreateTime != b.CreateTime {
-// log.ZDebug(context.Background(), "user equal", "a", a.CreateTime, "b", b.CreateTime)
-// }
-// if a.UserID != b.UserID {
-// log.ZDebug(context.Background(), "user equal", "a", a.UserID, "b", b.UserID)
-// }
-// if a.Ex != b.Ex {
-// log.ZDebug(context.Background(), "user equal", "a", a.Ex, "b", b.Ex)
-// }
-//
-// if a.Nickname != b.Nickname {
-// log.ZDebug(context.Background(), "user equal", "a", a.Nickname, "b", b.Nickname)
-// }
-// if a.FaceURL != b.FaceURL {
-// log.ZDebug(context.Background(), "user equal", "a", a.FaceURL, "b", b.FaceURL)
-// }
-// if a.AttachedInfo != b.AttachedInfo {
-// log.ZDebug(context.Background(), "user equal", "a", a.AttachedInfo, "b", b.AttachedInfo)
-// }
-// if a.GlobalRecvMsgOpt != b.GlobalRecvMsgOpt {
-// log.ZDebug(context.Background(), "user equal", "a", a.GlobalRecvMsgOpt, "b", b.GlobalRecvMsgOpt)
-// }
-// if a.AppMangerLevel != b.AppMangerLevel {
-// log.ZDebug(context.Background(), "user equal", "a", a.AppMangerLevel, "b", b.AppMangerLevel)
-// }
-// return a.UserID == b.UserID && a.Nickname == b.Nickname && a.FaceURL == b.FaceURL &&
-// a.CreateTime == b.CreateTime && a.AttachedInfo == b.AttachedInfo &&
-// a.Ex == b.Ex && a.GlobalRecvMsgOpt == b.GlobalRecvMsgOpt && a.AppMangerLevel == b.AppMangerLevel
-//}
-
-// DoNotification handles incoming notifications for the user.
-func (u *User) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "user notification", "msg", *msg)
- go func() {
- switch msg.ContentType {
- case constant.UserInfoUpdatedNotification:
- u.userInfoUpdatedNotification(ctx, msg)
- case constant.UserStatusChangeNotification:
- u.userStatusChangeNotification(ctx, msg)
- case constant.UserCommandAddNotification:
- u.userCommandAddNotification(ctx, msg)
- case constant.UserCommandDeleteNotification:
- u.userCommandDeleteNotification(ctx, msg)
- case constant.UserCommandUpdateNotification:
- u.userCommandUpdateNotification(ctx, msg)
- default:
- // log.Error(operationID, "type failed ", msg.ClientMsgID, msg.ServerMsgID, msg.ContentType)
- }
- }()
-}
-
-// userInfoUpdatedNotification handles notifications about updated user information.
-func (u *User) userInfoUpdatedNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "userInfoUpdatedNotification", "msg", *msg)
- tips := sdkws.UserInfoUpdatedTips{}
- if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
- log.ZError(ctx, "comm.UnmarshalTips failed", err, "msg", msg.Content)
- return
- }
-
- if tips.UserID == u.loginUserID {
- u.SyncLoginUserInfo(ctx)
- } else {
- log.ZDebug(ctx, "detail.UserID != u.loginUserID, do nothing", "detail.UserID", tips.UserID, "u.loginUserID", u.loginUserID)
- }
-}
-
-// userStatusChangeNotification get subscriber status change callback
-func (u *User) userStatusChangeNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "userStatusChangeNotification", "msg", *msg)
- tips := sdkws.UserStatusChangeTips{}
- if err := utils.UnmarshalNotificationElem(msg.Content, &tips); err != nil {
- log.ZError(ctx, "comm.UnmarshalTips failed", err, "msg", msg.Content)
- return
- }
- if tips.FromUserID == u.loginUserID {
- log.ZDebug(ctx, "self terminal login", "tips", tips)
- return
- }
- u.SyncUserStatus(ctx, tips.FromUserID, tips.Status, tips.PlatformID)
-}
-
-// userCommandAddNotification handle notification when user add favorite
-func (u *User) userCommandAddNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "userCommandAddNotification", "msg", *msg)
- tip := sdkws.UserCommandAddTips{}
- if tip.ToUserID == u.loginUserID {
- u.SyncAllCommand(ctx)
- } else {
- log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
- }
-}
-
-// userCommandDeleteNotification handle notification when user delete favorite
-func (u *User) userCommandDeleteNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "userCommandAddNotification", "msg", *msg)
- tip := sdkws.UserCommandDeleteTips{}
- if tip.ToUserID == u.loginUserID {
- u.SyncAllCommand(ctx)
- } else {
- log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
- }
-}
-
-// userCommandUpdateNotification handle notification when user update favorite
-func (u *User) userCommandUpdateNotification(ctx context.Context, msg *sdkws.MsgData) {
- log.ZDebug(ctx, "userCommandAddNotification", "msg", *msg)
- tip := sdkws.UserCommandUpdateTips{}
- if tip.ToUserID == u.loginUserID {
- u.SyncAllCommand(ctx)
- } else {
- log.ZDebug(ctx, "ToUserID != u.loginUserID, do nothing", "detail.UserID", tip.ToUserID, "u.loginUserID", u.loginUserID)
- }
+func (u *User) GetUserInfoWithCache(ctx context.Context, cacheKey string) (*model_struct.LocalUser, error) {
+ return u.UserCache.Fetch(ctx, cacheKey)
}
-// GetUsersInfoFromSvr retrieves user information from the server.
-func (u *User) GetUsersInfoFromSvr(ctx context.Context, userIDs []string) ([]*model_struct.LocalUser, error) {
- resp, err := util.CallApi[userPb.GetDesignateUsersResp](ctx, constant.GetUsersInfoRouter, userPb.GetDesignateUsersReq{UserIDs: userIDs})
+func (u *User) GetUsersInfoWithCache(ctx context.Context, cacheKeys []string) ([]*model_struct.LocalUser, error) {
+ m, err := u.UserCache.BatchFetch(ctx, cacheKeys)
if err != nil {
- return nil, sdkerrs.WrapMsg(err, "GetUsersInfoFromSvr failed")
+ return nil, err
}
- return datautil.Batch(ServerUserToLocalUser, resp.UsersInfo), nil
+ return datautil.Values(m), nil
}
-// GetSingleUserFromSvr retrieves user information from the server.
-func (u *User) GetSingleUserFromSvr(ctx context.Context, userID string) (*model_struct.LocalUser, error) {
- users, err := u.GetUsersInfoFromSvr(ctx, []string{userID})
+// GetSingleUserFromServer retrieves user information from the server.
+func (u *User) GetSingleUserFromServer(ctx context.Context, userID string) (*model_struct.LocalUser, error) {
+ users, err := u.getUsersInfo(ctx, []string{userID})
if err != nil {
return nil, err
}
if len(users) > 0 {
- return users[0], nil
+ return ServerUserToLocalUser(users[0]), nil
}
return nil, sdkerrs.ErrUserIDNotFound.WrapMsg(fmt.Sprintf("getSelfUserInfo failed, userID: %s not exist", userID))
}
-// getSelfUserInfo retrieves the user's information.
-func (u *User) getSelfUserInfo(ctx context.Context) (*model_struct.LocalUser, error) {
- userInfo, errLocal := u.GetLoginUser(ctx, u.loginUserID)
- if errLocal != nil {
- srvUserInfo, errServer := u.GetServerUserInfo(ctx, []string{u.loginUserID})
- if errServer != nil {
- return nil, errServer
- }
- if len(srvUserInfo) == 0 {
- return nil, sdkerrs.ErrUserIDNotFound
- }
- userInfo = ServerUserToLocalUser(srvUserInfo[0])
- _ = u.InsertLoginUser(ctx, userInfo)
- }
- return userInfo, nil
-}
-
-// updateSelfUserInfo updates the user's information.
-func (u *User) updateSelfUserInfo(ctx context.Context, userInfo *sdkws.UserInfo) error {
- userInfo.UserID = u.loginUserID
- if err := util.ApiPost(ctx, constant.UpdateSelfUserInfoRouter, userPb.UpdateUserInfoReq{UserInfo: userInfo}, nil); err != nil {
- return err
- }
- _ = u.SyncLoginUserInfo(ctx)
- return nil
-}
-
-// updateSelfUserInfoEx updates the user's information with Ex field.
-func (u *User) updateSelfUserInfoEx(ctx context.Context, userInfo *sdkws.UserInfoWithEx) error {
- userInfo.UserID = u.loginUserID
- if err := util.ApiPost(ctx, constant.UpdateSelfUserInfoExRouter, userPb.UpdateUserInfoExReq{UserInfo: userInfo}, nil); err != nil {
- return err
- }
- _ = u.SyncLoginUserInfo(ctx)
- return nil
-}
-
-// CRUD user command
-func (u *User) ProcessUserCommandAdd(ctx context.Context, userCommand *userPb.ProcessUserCommandAddReq) error {
- if err := util.ApiPost(ctx, constant.ProcessUserCommandAdd, userPb.ProcessUserCommandAddReq{UserID: u.loginUserID, Type: userCommand.Type, Uuid: userCommand.Uuid, Value: userCommand.Value}, nil); err != nil {
- return err
- }
- return u.SyncAllCommand(ctx)
-
-}
-
-// ProcessUserCommandDelete delete user's choice
-func (u *User) ProcessUserCommandDelete(ctx context.Context, userCommand *userPb.ProcessUserCommandDeleteReq) error {
- if err := util.ApiPost(ctx, constant.ProcessUserCommandDelete, userPb.ProcessUserCommandDeleteReq{UserID: u.loginUserID,
- Type: userCommand.Type, Uuid: userCommand.Uuid}, nil); err != nil {
- return err
- }
- return u.SyncAllCommand(ctx)
-}
+// GetUsersInfoFromServer retrieves user information from the server.
+func (u *User) GetUsersInfoFromServer(ctx context.Context, userIDs []string) ([]*model_struct.LocalUser, error) {
+ var err error
-// ProcessUserCommandUpdate update user's choice
-func (u *User) ProcessUserCommandUpdate(ctx context.Context, userCommand *userPb.ProcessUserCommandUpdateReq) error {
- if err := util.ApiPost(ctx, constant.ProcessUserCommandUpdate, userPb.ProcessUserCommandUpdateReq{UserID: u.loginUserID,
- Type: userCommand.Type, Uuid: userCommand.Uuid, Value: userCommand.Value}, nil); err != nil {
- return err
- }
- return u.SyncAllCommand(ctx)
-}
-
-// ProcessUserCommandGet get user's choice
-func (u *User) ProcessUserCommandGetAll(ctx context.Context) ([]*userPb.CommandInfoResp, error) {
- localCommands, err := u.DataBase.ProcessUserCommandGetAll(ctx)
- if err != nil {
- return nil, err // Handle the error appropriately
- }
-
- var result []*userPb.CommandInfoResp
- for _, localCommand := range localCommands {
- result = append(result, &userPb.CommandInfoResp{
- Type: localCommand.Type,
- CreateTime: localCommand.CreateTime,
- Uuid: localCommand.Uuid,
- Value: localCommand.Value,
- })
- }
-
- return result, nil
-}
-
-// ParseTokenFromSvr parses a token from the server.
-func (u *User) ParseTokenFromSvr(ctx context.Context) (int64, error) {
- resp, err := util.CallApi[authPb.ParseTokenResp](ctx, constant.ParseTokenRouter, authPb.ParseTokenReq{})
- return resp.ExpireTimeSeconds, err
-}
-
-// GetServerUserInfo retrieves user information from the server.
-func (u *User) GetServerUserInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
- resp, err := util.CallApi[userPb.GetDesignateUsersResp](ctx, constant.GetUsersInfoRouter, &userPb.GetDesignateUsersReq{UserIDs: userIDs})
+ serverUsersInfo, err := u.getUsersInfo(ctx, userIDs)
if err != nil {
return nil, err
}
- return resp.UsersInfo, nil
-}
-// subscribeUsersStatus Presence status of subscribed users.
-func (u *User) subscribeUsersStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
- resp, err := util.CallApi[userPb.SubscribeOrCancelUsersStatusResp](ctx, constant.SubscribeUsersStatusRouter, &userPb.SubscribeOrCancelUsersStatusReq{
- UserID: u.loginUserID,
- UserIDs: userIDs,
- Genre: PbConstant.SubscriberUser,
- })
- if err != nil {
+ if len(serverUsersInfo) == 0 {
+ log.ZError(ctx, "serverUsersInfo is empty", err, "userIDs", userIDs)
return nil, err
}
- return resp.StatusList, nil
-}
-// unsubscribeUsersStatus Unsubscribe a user's presence.
-func (u *User) unsubscribeUsersStatus(ctx context.Context, userIDs []string) error {
- _, err := util.CallApi[userPb.SubscribeOrCancelUsersStatusResp](ctx, constant.SubscribeUsersStatusRouter, &userPb.SubscribeOrCancelUsersStatusReq{
- UserID: u.loginUserID,
- UserIDs: userIDs,
- Genre: PbConstant.Unsubscribe,
- })
- if err != nil {
- return err
- }
- return nil
-}
-
-// getSubscribeUsersStatus Get the online status of subscribers.
-func (u *User) getSubscribeUsersStatus(ctx context.Context) ([]*userPb.OnlineStatus, error) {
- resp, err := util.CallApi[userPb.GetSubscribeUsersStatusResp](ctx, constant.GetSubscribeUsersStatusRouter, &userPb.GetSubscribeUsersStatusReq{
- UserID: u.loginUserID,
- })
- if err != nil {
- return nil, err
- }
- return resp.StatusList, nil
-}
-
-// getUserStatus Get the online status of users.
-func (u *User) getUserStatus(ctx context.Context, userIDs []string) ([]*userPb.OnlineStatus, error) {
- resp, err := util.CallApi[userPb.GetUserStatusResp](ctx, constant.GetUserStatusRouter, &userPb.GetUserStatusReq{
- UserID: u.loginUserID,
- UserIDs: userIDs,
- })
- if err != nil {
- return nil, err
- }
- return resp.StatusList, nil
+ return datautil.Batch(ServerUserToLocalUser, serverUsersInfo), nil
}
diff --git a/internal/util/notice.go b/internal/util/notice.go
deleted file mode 100644
index 06ec7f187..000000000
--- a/internal/util/notice.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package util
-
-//func NoticeChange[T any](fn func(data string)) func(ctx context.Context, state int, value T) error {
-// return func(ctx context.Context, state int, value T) error {
-// if state != syncer.Unchanged {
-// data, err := json.Marshal(value)
-// if err != nil {
-// return err
-// }
-// fn(string(data))
-// }
-// return nil
-// }
-//}
diff --git a/internal/util/post.go b/internal/util/post.go
deleted file mode 100644
index 49414546a..000000000
--- a/internal/util/post.go
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package util
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/page"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/tools/errs"
- "io"
- "net/http"
- "time"
-
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-// apiClient is a global HTTP client with a timeout of one minute.
-var apiClient = &http.Client{
- Timeout: time.Second * 30,
-}
-
-// ApiResponse represents the standard structure of an API response.
-type ApiResponse struct {
- ErrCode int `json:"errCode"`
- ErrMsg string `json:"errMsg"`
- ErrDlt string `json:"errDlt"`
- Data json.RawMessage `json:"data"`
-}
-
-// ApiPost performs an HTTP POST request to a specified API endpoint.
-// It serializes the request object, sends it to the API, and unmarshals the response into the resp object.
-// It handles logging, error wrapping, and operation ID validation.
-// Context (ctx) is used for passing metadata and control information.
-// api: the API endpoint to which the request is sent.
-// req: the request object to be sent to the API.
-// resp: a pointer to the response object where the API response will be unmarshalled.
-// Returns an error if the request fails at any stage.
-func ApiPost(ctx context.Context, api string, req, resp any) (err error) {
- // Extract operationID from context and validate.
- operationID, _ := ctx.Value("operationID").(string)
- if operationID == "" {
- err := sdkerrs.ErrArgs.WrapMsg("call api operationID is empty")
- log.ZError(ctx, "ApiRequest", err, "type", "ctx not set operationID")
- return err
- }
-
- // Deferred function to log the result of the API call.
- defer func(start time.Time) {
- elapsed := time.Since(start).Milliseconds()
- if err == nil {
- log.ZDebug(ctx, "CallApi", "api", api, "state", "success", "cost time", fmt.Sprintf("%dms", elapsed))
- } else {
- log.ZError(ctx, "CallApi", err, "api", api, "state", "failed", "cost time", fmt.Sprintf("%dms", elapsed))
- }
- }(time.Now())
-
- // Serialize the request object to JSON.
- reqBody, err := json.Marshal(req)
- if err != nil {
- log.ZError(ctx, "ApiRequest", err, "type", "json.Marshal(req) failed")
- return sdkerrs.ErrSdkInternal.WrapMsg("json.Marshal(req) failed " + err.Error())
- }
-
- // Construct the full API URL and create a new HTTP request with context.
- ctxInfo := ccontext.Info(ctx)
- reqUrl := ctxInfo.ApiAddr() + api
- request, err := http.NewRequestWithContext(ctx, http.MethodPost, reqUrl, bytes.NewReader(reqBody))
- if err != nil {
- log.ZError(ctx, "ApiRequest", err, "type", "http.NewRequestWithContext failed")
- return sdkerrs.ErrSdkInternal.WrapMsg("sdk http.NewRequestWithContext failed " + err.Error())
- }
-
- // Set headers for the request.
- log.ZDebug(ctx, "ApiRequest", "url", reqUrl, "body", string(reqBody))
- request.ContentLength = int64(len(reqBody))
- request.Header.Set("Content-Type", "application/json")
- request.Header.Set("operationID", operationID)
- request.Header.Set("token", ctxInfo.Token())
-
- // Send the request and receive the response.
- response, err := apiClient.Do(request)
- if err != nil {
- log.ZError(ctx, "ApiRequest", err, "type", "network error")
- return sdkerrs.ErrNetwork.WrapMsg("ApiPost http.Client.Do failed " + err.Error())
- }
-
- // Ensure the response body is closed after processing.
- defer response.Body.Close()
-
- // Read the response body.
- respBody, err := io.ReadAll(response.Body)
- if err != nil {
- log.ZError(ctx, "ApiResponse", err, "type", "read body", "status", response.Status)
- return sdkerrs.ErrSdkInternal.WrapMsg("io.ReadAll(ApiResponse) failed " + err.Error())
- }
-
- // Log the response for debugging purposes.
- log.ZDebug(ctx, "ApiResponse", "url", reqUrl, "status", response.Status, "body", string(respBody))
-
- // Unmarshal the response body into the ApiResponse structure.
- var baseApi ApiResponse
- if err := json.Unmarshal(respBody, &baseApi); err != nil {
- log.ZError(ctx, "ApiResponse", err, "type", "api code parse")
- return sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("api %s json.Unmarshal(%q, %T) failed %s", api, string(respBody), &baseApi, err.Error()))
- }
-
- // Check if the API returned an error code and handle it.
- if baseApi.ErrCode != 0 {
- err := sdkerrs.New(baseApi.ErrCode, baseApi.ErrMsg, baseApi.ErrDlt)
- ccontext.GetApiErrCodeCallback(ctx).OnError(ctx, err)
- log.ZError(ctx, "ApiResponse", err, "type", "api code error", "msg", baseApi.ErrMsg, "dlt", baseApi.ErrDlt)
- return err
- }
-
- // If no data is received, or it's null, return with no error.
- if resp == nil || len(baseApi.Data) == 0 || string(baseApi.Data) == "null" {
- return nil
- }
-
- // Unmarshal the actual data part of the response into the provided response object.
- if err := json.Unmarshal(baseApi.Data, resp); err != nil {
- log.ZError(ctx, "ApiResponse", err, "type", "api data parse", "data", string(baseApi.Data), "bind", fmt.Sprintf("%T", resp))
- return sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("json.Unmarshal(%q, %T) failed %s", string(baseApi.Data), resp, err.Error()))
- }
-
- return nil
-}
-
-// CallApi wraps ApiPost to make an API call and unmarshal the response into a new instance of type T.
-func CallApi[T any](ctx context.Context, api string, req any) (*T, error) {
- var resp T
- if err := ApiPost(ctx, api, req, &resp); err != nil {
- return nil, err
- }
- return &resp, nil
-}
-
-// GetPageAll handles pagination for API requests. It iterates over pages of data until all data is retrieved.
-// A is the request type with pagination support, B is the response type, and C is the type of data to be returned.
-// The function fn processes each page of response data to extract a slice of C.
-func GetPageAll[A interface {
- GetPagination() *sdkws.RequestPagination
-}, B, C any](ctx context.Context, api string, req A, fn func(resp *B) []C) ([]C, error) {
- if req.GetPagination().ShowNumber <= 0 {
- req.GetPagination().ShowNumber = 50
- }
- var res []C
- for i := int32(0); ; i++ {
- req.GetPagination().PageNumber = i + 1
- memberResp, err := CallApi[B](ctx, api, req)
- if err != nil {
- return nil, err
- }
- list := fn(memberResp)
- res = append(res, list...)
- if len(list) < int(req.GetPagination().ShowNumber) {
- break
- }
- }
- return res, nil
-}
-
-func GetPageAllWithMaxNum[A interface {
- GetPagination() *sdkws.RequestPagination
-}, B, C any](ctx context.Context, api string, req A, fn func(resp *B) []C, maxItems int) ([]C, error) {
- if req.GetPagination().ShowNumber <= 0 {
- req.GetPagination().ShowNumber = 50
- }
- var res []C
- totalFetched := 0
- for i := int32(0); ; i++ {
- req.GetPagination().PageNumber = i + 1
- memberResp, err := CallApi[B](ctx, api, req)
- if err != nil {
- return nil, err
- }
- list := fn(memberResp)
- res = append(res, list...)
- totalFetched += len(list)
- if len(list) < int(req.GetPagination().ShowNumber) || (maxItems > 0 && totalFetched >= maxItems) {
- break
- }
- }
- if maxItems > 0 && len(res) > maxItems {
- res = res[:maxItems]
- }
- return res, nil
-}
-
-func FetchAndInsertPagedData[RESP, L any](ctx context.Context, api string, req page.PageReq, fn func(resp *RESP) []L, batchInsertFn func(ctx context.Context, items []L) error,
- insertFn func(ctx context.Context, item L) error, maxItems int) error {
- if req.GetPagination().ShowNumber <= 0 {
- req.GetPagination().ShowNumber = 50
- }
- var errSingle error
- var errList []error
- totalFetched := 0
- for i := int32(0); ; i++ {
- req.GetPagination().PageNumber = i + 1
- memberResp, err := CallApi[RESP](ctx, api, req)
- if err != nil {
- return err
- }
- list := fn(memberResp)
- if err := batchInsertFn(ctx, list); err != nil {
- for _, item := range list {
- errSingle = insertFn(ctx, item)
- if errSingle != nil {
- errList = append(errList, errs.New(errSingle.Error(), "item", item))
- }
- }
- }
- totalFetched += len(list)
- if len(list) < int(req.GetPagination().ShowNumber) || (maxItems > 0 && totalFetched >= maxItems) {
- break
- }
- }
- if len(errList) > 0 {
- return errs.WrapMsg(errList[0], "batch insert failed due to data exception")
- }
- return nil
-}
diff --git a/msgtest/config.go b/msgtest/config.go
index 74edf1bd3..eb7b24c6f 100644
--- a/msgtest/config.go
+++ b/msgtest/config.go
@@ -2,8 +2,9 @@ package msgtest
import (
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/constant"
)
// config here
@@ -11,16 +12,13 @@ import (
// system
var (
TESTIP = "127.0.0.1"
- APIADDR = fmt.Sprintf("http://%v:20002", TESTIP)
- WSADDR = fmt.Sprintf("ws://%v:20001", TESTIP)
+ APIADDR = fmt.Sprintf("http://%v:10002", TESTIP)
+ WSADDR = fmt.Sprintf("ws://%v:10001", TESTIP)
SECRET = "openIM123"
MANAGERUSERID = "openIMAdmin"
PLATFORMID = constant.WindowsPlatformID
LogLevel = uint32(5)
-
- REGISTERADDR = APIADDR + constant.UserRegister
- TOKENADDR = APIADDR + constant.GetUsersToken
)
func GetConfig() *sdk_struct.IMConfig {
diff --git a/msgtest/main/main.go b/msgtest/main/main.go
index f3d48deb5..e551eec06 100644
--- a/msgtest/main/main.go
+++ b/msgtest/main/main.go
@@ -3,7 +3,7 @@ package main
import (
"context"
"flag"
- "github.com/openimsdk/openim-sdk-core/v3/version"
+ "fmt"
log2 "log"
"net/http"
_ "net/http/pprof"
@@ -13,6 +13,8 @@ import (
"syscall"
"time"
+ "github.com/openimsdk/openim-sdk-core/v3/version"
+
"github.com/openimsdk/openim-sdk-core/v3/msgtest/module"
"github.com/openimsdk/tools/log"
)
@@ -20,26 +22,26 @@ import (
func init() {
_ = runtime.GOMAXPROCS(7)
InitWithFlag()
- if err := log.InitFromConfig("sdk.log", "sdk", 3,
- true, false, "./", 2, 24, version.Version); err != nil {
+ if err := log.InitLoggerFromConfig("sdk.log", "sdk", "", "", 3,
+ true, false, "./", 2, 24, version.Version, false); err != nil {
panic(err)
}
}
var (
- totalOnlineUserNum int // 总在线用户数
- randomSender int // 随机发送者数
- randomReceiver int // 随机接收者数
- singleSamplingRate float64 // 单聊抽样率
- GroupSenderRate float64 // 群聊随机的发送者比例
- GroupOnlineRate float64 //group chat online user rate
+ totalOnlineUserNum int // total online users num
+ randomSender int // random sender num
+ randomReceiver int // random receiver num
+ singleSamplingRate float64 // sampling rate for single chat
+ GroupSenderRate float64 // the random sender ratio for group chats
+ GroupOnlineRate float64 // group chat online user rate
start int
end int
count int
sendInterval int
- //recvMsgUserNum int // 消息接收者数, 抽样账号
- isRegisterUser bool // 是否注册用户
+ //recvMsgUserNum int // the number of message recipients, sampled accounts
+ isRegisterUser bool // If register users
onlineUsersOnly bool
pprofEnable bool
@@ -98,9 +100,11 @@ func main() {
log2.Println(http.ListenAndServe("0.0.0.0:6060", nil))
}()
}
- p := module.NewPressureTester()
+ p, err := module.NewPressureTester()
+ if err != nil {
+ fmt.Println(err)
+ }
var f, r, o []string
- var err error
if start != 0 {
f, r, o, err = p.SelectSampleFromStarEnd(start, end, singleSamplingRate)
} else {
diff --git a/msgtest/module/api_msg_sender.go b/msgtest/module/api_msg_sender.go
index d16596597..0a1b8e644 100644
--- a/msgtest/module/api_msg_sender.go
+++ b/msgtest/module/api_msg_sender.go
@@ -2,10 +2,11 @@ package module
import (
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
-
+ "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
)
@@ -47,5 +48,5 @@ func (a *ApiMsgSender) SendMsg(sendID, recvID string, index int) error {
},
}
var resp msg.SendMsgResp
- return a.postWithCtx(constant.SendMsgRouter, req, &resp)
+ return a.postWithCtx(api.SendMsg.Route(), req, &resp)
}
diff --git a/msgtest/module/friend_manager.go b/msgtest/module/friend_manager.go
index 675699f76..8956db251 100644
--- a/msgtest/module/friend_manager.go
+++ b/msgtest/module/friend_manager.go
@@ -1,7 +1,7 @@
package module
import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/protocol/relation"
)
@@ -14,5 +14,5 @@ func (t *TestFriendManager) ImportFriends(ownerUserID string, friendUserIDs []st
OwnerUserID: ownerUserID,
FriendUserIDs: friendUserIDs,
}
- return t.postWithCtx(constant.ImportFriendListRouter, &req, nil)
+ return t.postWithCtx(api.ImportFriendList.Route(), &req, nil)
}
diff --git a/msgtest/module/group_manager.go b/msgtest/module/group_manager.go
index 3dc9033cf..af409ea6c 100644
--- a/msgtest/module/group_manager.go
+++ b/msgtest/module/group_manager.go
@@ -3,6 +3,7 @@ package module
import (
"context"
"fmt"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"time"
@@ -37,7 +38,7 @@ func (t *TestGroupManager) CreateGroup(groupID string, groupName string, ownerUs
},
}
resp := &group.CreateGroupResp{}
- if err := t.postWithCtx(constant.CreateGroupRouter, &req, &resp); err != nil {
+ if err := t.postWithCtx(api.CreateGroup.Route(), &req, &resp); err != nil {
return err
}
if len(userIDs) > batch {
@@ -57,7 +58,7 @@ func (t *TestGroupManager) CreateGroup(groupID string, groupName string, ownerUs
"reason": "test",
}
resp := struct{}{}
- if err := t.postWithCtx(constant.RouterGroup+"/invite_user_to_group", req, &resp); err != nil {
+ if err := t.postWithCtx(api.InviteUserToGroup.Route(), req, &resp); err != nil {
return err
}
}
@@ -71,5 +72,5 @@ func (t *TestGroupManager) InviteUserToGroup(ctx context.Context, groupID string
InvitedUserIDs: invitedUserIDs,
}
resp := &group.InviteUserToGroupResp{}
- return t.postWithCtx(constant.InviteUserToGroupRouter, &req, &resp)
+ return t.postWithCtx(api.InviteUserToGroup.Route(), &req, &resp)
}
diff --git a/msgtest/module/manager.go b/msgtest/module/manager.go
index 9db74010f..b5a8a2073 100644
--- a/msgtest/module/manager.go
+++ b/msgtest/module/manager.go
@@ -9,8 +9,9 @@ import (
"net/http"
"time"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
authPB "github.com/openimsdk/protocol/auth"
@@ -89,7 +90,7 @@ func (m *MetaManager) apiPost(ctx context.Context, route string, req, resp any)
}
log.ZDebug(ctx, "ApiResponse", "url", reqUrl, "status", response.Status,
"body", string(respBody), "time", time.Since(start).Milliseconds())
- var baseApi util.ApiResponse
+ var baseApi network.ApiResponse
if err := json.Unmarshal(respBody, &baseApi); err != nil {
return sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("api %s json.Unmarshal(%q, %T) failed %s", m.apiAddr, string(respBody), &baseApi, err.Error()))
}
@@ -114,10 +115,20 @@ func (m *MetaManager) buildCtx() context.Context {
return mcontext.NewCtx(utils.OperationIDGenerator())
}
-func (m *MetaManager) getToken(userID string, platformID int32) (string, error) {
- req := authPB.UserTokenReq{PlatformID: platformID, UserID: userID, Secret: m.secret}
- resp := authPB.UserTokenResp{}
- err := m.postWithCtx(constant.GetUsersToken, &req, &resp)
+func (m *MetaManager) getAdminToken(userID string) (string, error) {
+ req := authPB.GetAdminTokenReq{UserID: userID, Secret: m.secret}
+ resp := authPB.GetAdminTokenResp{}
+ err := m.postWithCtx(api.GetAdminToken.Route(), &req, &resp)
+ if err != nil {
+ return "", err
+ }
+ return resp.Token, nil
+}
+
+func (m *MetaManager) getUserToken(userID string, platform int32) (string, error) {
+ req := authPB.GetUserTokenReq{UserID: userID, PlatformID: platform}
+ resp := authPB.GetUserTokenResp{}
+ err := m.postWithCtx(api.GetUsersToken.Route(), &req, &resp)
if err != nil {
return "", err
}
@@ -125,7 +136,7 @@ func (m *MetaManager) getToken(userID string, platformID int32) (string, error)
}
func (m *MetaManager) initToken() error {
- token, err := m.getToken(m.managerUserID, constant.AdminPlatformID)
+ token, err := m.getAdminToken(m.managerUserID)
if err != nil {
return err
}
@@ -135,7 +146,7 @@ func (m *MetaManager) initToken() error {
func (m *MetaManager) GetServerTime() (int64, error) {
req := msg.GetServerTimeReq{}
resp := msg.GetServerTimeResp{}
- err := m.postWithCtx(constant.GetServerTimeRouter, &req, &resp)
+ err := m.postWithCtx(api.GetServerTime.Route(), &req, &resp)
if err != nil {
return 0, err
} else {
diff --git a/msgtest/module/msg_sender.go b/msgtest/module/msg_sender.go
index fe7588e0a..fcbf5d77a 100644
--- a/msgtest/module/msg_sender.go
+++ b/msgtest/module/msg_sender.go
@@ -15,17 +15,17 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
-
+ pbconstant "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext"
)
var (
- qpsCounter int64 // 全局变量用于统计请求数
- qpsMutex sync.Mutex // 互斥锁用于保护全局变量的并发访问
- qpsUpdateTime time.Time // 全局变量用于记录上次更新时间
- QPSChan chan int64 // 用于定时更新qpsCounter的channel
+ qpsCounter int64 // Global variable used to count the number of requests
+ qpsMutex sync.Mutex // Mutex to protect concurrent access to the global variable
+ qpsUpdateTime time.Time // Global variable to track the last update time
+ QPSChan chan int64 // Channel used for periodic updates to qpsCounter
)
//func init() {
@@ -37,7 +37,7 @@ func IncrementQPS() {
defer qpsMutex.Unlock()
now := time.Now()
- // 如果距离上次更新时间超过1秒,则重置计数器
+ // If more than 1 second has passed since the last update, reset the counter.
if now.Sub(qpsUpdateTime) >= time.Second {
QPSChan <- qpsCounter
qpsCounter = 0
@@ -129,7 +129,7 @@ func newUserCtx(userID, token string, imConfig sdk_struct.IMConfig) context.Cont
func NewUser(userID, token string, timeOffset int64, p *PressureTester, imConfig sdk_struct.IMConfig, opts ...func(core *SendMsgUser)) *SendMsgUser {
pushMsgAndMaxSeqCh := make(chan common.Cmd2Value, 1000)
ctx := newUserCtx(userID, token, imConfig)
- longConnMgr := interaction.NewLongConnMgr(ctx, &ConnListner{}, nil, pushMsgAndMaxSeqCh, nil)
+ longConnMgr := interaction.NewLongConnMgr(ctx, &ConnListener{}, func(m map[string][]int32) {}, pushMsgAndMaxSeqCh, nil)
core := &SendMsgUser{
pushMsgAndMaxSeqCh: pushMsgAndMaxSeqCh,
longConnMgr: longConnMgr,
@@ -154,6 +154,10 @@ func NewUser(userID, token string, timeOffset int64, p *PressureTester, imConfig
return core
}
+func (b *SendMsgUser) LongConnMgr() *interaction.LongConnMgr {
+ return b.longConnMgr
+}
+
func (b *SendMsgUser) Close(ctx context.Context) {
b.longConnMgr.Close(ctx)
b.cancelFunc()
@@ -185,12 +189,12 @@ func (b *SendMsgUser) BatchSendSingleMsg(ctx context.Context, userID string, ind
}
func (b *SendMsgUser) SendGroupMsg(ctx context.Context, groupID string, index int) error {
- return b.sendMsg(ctx, "", groupID, index, constant.SuperGroupChatType, fmt.Sprintf("this is test msg user %s to group %s, index: %d", b.userID, groupID, index))
+ return b.sendMsg(ctx, "", groupID, index, constant.ReadGroupChatType, fmt.Sprintf("this is test msg user %s to group %s, index: %d", b.userID, groupID, index))
}
func (b *SendMsgUser) BatchSendGroupMsg(ctx context.Context, groupID string, index int) error {
content := fmt.Sprintf("this is test msg user %s to group %s, index: %d", b.userID, groupID, index)
- err := b.sendMsg(ctx, "", groupID, index, constant.SuperGroupChatType, content)
+ err := b.sendMsg(ctx, "", groupID, index, constant.ReadGroupChatType, content)
if err != nil {
log.ZError(ctx, "send msg failed", err, "groupID", groupID, "index", index, "content", content)
//b.singleFailedMessageMap[content] = err
@@ -211,7 +215,7 @@ func (b *SendMsgUser) sendMsg(ctx context.Context, userID, groupID string, index
SenderNickname: b.userID,
Content: []byte(utils.StructToJsonString(text)),
CreateTime: time.Now().UnixMilli(),
- SenderPlatformID: constant.AdminPlatformID,
+ SenderPlatformID: pbconstant.AdminPlatformID,
ClientMsgID: clientMsgID,
}
// IncrementQPS()
@@ -222,7 +226,7 @@ func (b *SendMsgUser) sendMsg(ctx context.Context, userID, groupID string, index
b.singleFailedMessageMap[clientMsgID] = &errorValue{err: err,
SendID: b.userID, RecvID: userID, MsgID: clientMsgID, OperationID: mcontext.GetOperationID(ctx)}
log.ZError(ctx, "send single msg failed", err, "userID", userID, "index", index, "content", content)
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
b.groupFailedMessageMap[groupID] = append(b.groupFailedMessageMap[groupID], &errorValue{err: err,
SendID: b.userID, RecvID: groupID, MsgID: clientMsgID, GroupID: groupID, OperationID: mcontext.GetOperationID(ctx)})
log.ZError(ctx, "send group msg failed", err, "groupID", groupID, "index", index, "content", content)
@@ -241,7 +245,7 @@ func (b *SendMsgUser) sendMsg(ctx context.Context, userID, groupID string, index
sendTime: msg.SendTime,
}
}
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
b.groupSendSampleNum[groupID]++
}
@@ -287,7 +291,7 @@ func (b *SendMsgUser) defaultRecvPushMsgCallback(ctx context.Context, msg *sdkws
Latency: b.GetRelativeServerTime() - msg.SendTime,
}
}
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
if b.userID == b.p.groupOwnerUserID[msg.GroupID] {
b.groupMessage++
log.ZWarn(context.Background(), "recv message", nil, "userID", b.userID,
@@ -319,14 +323,40 @@ func (b *SendMsgUser) GetRelativeServerTime() int64 {
return utils.GetCurrentTimestampByMill()
}
-type ConnListner struct {
-}
+type ConnListener struct{}
-func (c *ConnListner) OnConnecting() {}
-func (c *ConnListner) OnConnectSuccess() {}
-func (c *ConnListner) OnConnectFailed(errCode int32, errMsg string) {
+func (c *ConnListener) OnConnecting() {}
+func (c *ConnListener) OnConnectSuccess() {}
+func (c *ConnListener) OnConnectFailed(errCode int32, errMsg string) {
// log.ZError(context.Background(), "connect failed", nil, "errCode", errCode, "errMsg", errMsg)
}
-func (c *ConnListner) OnKickedOffline() {}
-func (c *ConnListner) OnUserTokenExpired() {}
-func (c *ConnListner) OnUserTokenInvalid(errMsg string) {}
+func (c *ConnListener) OnKickedOffline() {}
+func (c *ConnListener) OnUserTokenExpired() {}
+func (c *ConnListener) OnUserTokenInvalid(errMsg string) {}
+
+type UserListener struct{}
+
+func (u *UserListener) OnSelfInfoUpdated(userInfo string) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (u *UserListener) OnUserStatusChanged(userOnlineStatus string) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (u *UserListener) OnUserCommandAdd(userCommand string) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (u *UserListener) OnUserCommandDelete(userCommand string) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (u *UserListener) OnUserCommandUpdate(userCommand string) {
+ //TODO implement me
+ panic("implement me")
+}
diff --git a/msgtest/module/pressure.go b/msgtest/module/pressure.go
index d4f869e7b..2f547ad75 100644
--- a/msgtest/module/pressure.go
+++ b/msgtest/module/pressure.go
@@ -3,41 +3,39 @@ package module
import (
"context"
"fmt"
+ "github.com/openimsdk/tools/utils/datautil"
"math/rand"
"os"
"sync"
"sync/atomic"
"time"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/log"
)
var (
TESTIP = "127.0.0.1"
- APIADDR = fmt.Sprintf("http://%v:20002", TESTIP)
- WSADDR = fmt.Sprintf("ws://%v:20001", TESTIP)
+ APIADDR = fmt.Sprintf("http://%v:10002", TESTIP)
+ WSADDR = fmt.Sprintf("ws://%v:10001", TESTIP)
SECRET = "openIM123"
- MANAGERUSERID = "openIMAdmin"
+ MANAGERUSERID = "imAdmin"
PLATFORMID = constant.AndroidPlatformID
LogLevel = uint32(5)
-
- REGISTERADDR = APIADDR + constant.UserRegister
- TOKENADDR = APIADDR + constant.GetUsersToken
)
var (
- totalOnlineUserNum = 200000 // 总在线用户数
- friendMsgSenderNum = 200 // 好友消息发送者数
- NotFriendMsgSenderNum = 200 // 非好友消息发送者数
- groupMsgSenderNum = 200 // 群消息发送者数
- msgSenderNumEvreyUser = 100 // 每个用户的消息数
- fastenedUserNum = 600 // 固定用户数
-
- recvMsgUserNum = 20 // 消息接收者数, 抽样账号
+ totalOnlineUserNum = 200000 // total online users num
+ friendMsgSenderNum = 200 // friend msg sender num
+ NotFriendMsgSenderNum = 200 // not friend msg sender num
+ groupMsgSenderNum = 200 // group msg sender num
+ msgSenderNumEvreyUser = 100 // the number of messages per user.
+ fastenedUserNum = 600 // fixed number of users
+
+ recvMsgUserNum = 20 // the number of message recipients, sampled accounts
singleSampleUserList []string
)
@@ -56,7 +54,7 @@ const (
FiftyGroupNum = 100
TenGroupNum = 1000
- FastenedUserPrefix = "f"
+ FastenedUserPrefix = "test_v3_u"
OfflineUserPrefix = "o"
RecvMsgPrefix = "recv_msg_prefix"
singleMsgRecvPrefix = "single_msg_recv_prefix"
@@ -109,12 +107,14 @@ func (p *PressureTester) FormatGroupInfo(ctx context.Context) {
func (p *PressureTester) GetSingleSendNum() int64 {
return p.singleSendNum.Load()
}
-func NewPressureTester() *PressureTester {
+func NewPressureTester() (*PressureTester, error) {
metaManager := NewMetaManager(APIADDR, SECRET, MANAGERUSERID)
- metaManager.initToken()
+ if err := metaManager.initToken(); err != nil {
+ return nil, err
+ }
serverTime, err := metaManager.GetServerTime()
if err != nil {
- panic(err)
+ return nil, err
}
log.ZWarn(context.Background(), "server time is", nil, "serverTime", serverTime, "current time",
utils.GetCurrentTimestampByMill(), "time offset", serverTime-utils.GetCurrentTimestampByMill())
@@ -124,13 +124,13 @@ func NewPressureTester() *PressureTester {
msgSender: make(map[string]*SendMsgUser),
groupRandomSender: make(map[string][]string), groupOwnerUserID: make(map[string]string),
groupMemberNum: make(map[string]int),
- timeOffset: serverTime - utils.GetCurrentTimestampByMill()}
+ timeOffset: serverTime - utils.GetCurrentTimestampByMill()}, nil
}
func (p *PressureTester) genUserIDs() (userIDs, fastenedUserIDs, recvMsgUserIDs []string) {
- userIDs = p.userManager.GenUserIDs(totalOnlineUserNum - fastenedUserNum) // 在线用户
- fastenedUserIDs = p.userManager.GenUserIDsWithPrefix(fastenedUserNum, FastenedUserPrefix) // 指定发消息的固定用户
- recvMsgUserIDs = p.userManager.GenUserIDsWithPrefix(recvMsgUserNum, RecvMsgPrefix) // 抽样用户完整SDK
+ userIDs = p.userManager.GenUserIDs(totalOnlineUserNum - fastenedUserNum) // online users
+ fastenedUserIDs = p.userManager.GenUserIDsWithPrefix(fastenedUserNum, FastenedUserPrefix) // specify the fixed users for sending messages
+ recvMsgUserIDs = p.userManager.GenUserIDsWithPrefix(recvMsgUserNum, RecvMsgPrefix) // complete SDK for sampling users
return
}
@@ -160,7 +160,7 @@ func (p *PressureTester) SelectSampleFromStarEnd(start, end int, percentage floa
offlineUserIDs = p.userManager.GenSEUserIDsWithPrefix(end, 2*end-start, OfflineUserPrefix)
step := int(1.0 / percentage)
for i := start; i < end; i += step {
- sampleReceiver = append(sampleReceiver, fmt.Sprintf("%s_testv3_%d", FastenedUserPrefix, i))
+ sampleReceiver = append(sampleReceiver, fmt.Sprintf("%s%d", FastenedUserPrefix, i))
}
singleSampleUserList = sampleReceiver
return fastenedUserIDs, sampleReceiver, offlineUserIDs, nil
@@ -221,7 +221,7 @@ func (p *PressureTester) getGroup(fastenedUserIDs []string, groupMemberNum int,
offlineUserID := p.Shuffle(p.offlineUserIDs, groupMemberNum-olineUserIDNum)
ownerUserID = p.Shuffle(userIDs, 1)[0]
randomSender = p.Shuffle(userIDs, int(float64(groupMemberNum)*groupSenderRate))
- return ownerUserID, append(utils.RemoveOneInList(userIDs, ownerUserID), offlineUserID...), randomSender
+ return ownerUserID, append(datautil.DeleteElems(userIDs, ownerUserID), offlineUserID...), randomSender
}
func (p *PressureTester) CreateTestGroups(fastenedUserIDs []string, total int, groupSenderRate, groupOnlineRate float64, hundredThousandGroupNum, tenThousandGroupNum, thousandGroupNum,
diff --git a/msgtest/module/user_manager.go b/msgtest/module/user_manager.go
index f64e102a6..3a7c10926 100644
--- a/msgtest/module/user_manager.go
+++ b/msgtest/module/user_manager.go
@@ -2,7 +2,8 @@ package module
import (
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
"github.com/openimsdk/protocol/sdkws"
userPB "github.com/openimsdk/protocol/user"
)
@@ -20,13 +21,13 @@ func (t *TestUserManager) GenUserIDs(num int) (userIDs []string) {
func (t *TestUserManager) GenUserIDsWithPrefix(num int, prefix string) (userIDs []string) {
for i := 0; i < num; i++ {
- userIDs = append(userIDs, fmt.Sprintf("%s_testv3_%d", prefix, i))
+ userIDs = append(userIDs, fmt.Sprintf("%s%d", prefix, i))
}
return userIDs
}
func (t *TestUserManager) GenSEUserIDsWithPrefix(start, end int, prefix string) (userIDs []string) {
for i := start; i < end; i++ {
- userIDs = append(userIDs, fmt.Sprintf("%s_testv3_%d", prefix, i))
+ userIDs = append(userIDs, fmt.Sprintf("%s%d", prefix, i))
}
return userIDs
}
@@ -36,12 +37,11 @@ func (t *TestUserManager) RegisterUsers(userIDs ...string) error {
for _, userID := range userIDs {
users = append(users, &sdkws.UserInfo{UserID: userID, Nickname: userID})
}
- return t.postWithCtx(constant.UserRegister, &userPB.UserRegisterReq{
- Secret: t.secret,
- Users: users,
+ return t.postWithCtx(api.UserRegister.Route(), &userPB.UserRegisterReq{
+ Users: users,
}, nil)
}
func (t *TestUserManager) GetToken(userID string, platformID int32) (string, error) {
- return t.getToken(userID, platformID)
+ return t.getUserToken(userID, platformID)
}
diff --git a/msgtest/pressure_test.go b/msgtest/pressure_test.go
deleted file mode 100644
index 28b662e56..000000000
--- a/msgtest/pressure_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package msgtest
-
-//const (
-// TenThousandGroupUserNum = 10000
-// ThousandGroupUserNum = 1000
-// HundredGroupUserNum = 100
-// FiftyGroupUserNum = 50
-//
-// TenThousandGroupNum = 2
-// ThousandGroupNum = 5
-// HundredGroupNum = 50
-// FiftyGroupNum = 100
-//
-// FastenedUserPrefix = "fastened_user_prefix"
-// RecvMsgPrefix = "recv_msg_prefix"
-//)
-//
-//var (
-// totalOnlineUserNum int // 总在线用户数
-// friendMsgSenderNum int // 好友消息发送者数
-// NotFriendMsgSenderNum int // 非好友消息发送者数
-// groupMsgSenderNum int // 群消息发送者数
-// msgSenderNumEvreyUser int // 每个用户的消息数
-// fastenedUserNum int // 固定用户数
-//
-// recvMsgUserNum int // 消息接收者数, 抽样账号
-//
-//)
-//
-//func InitWithFlag() {
-// flag.IntVar(&totalOnlineUserNum, "t", 100000, "total online user num")
-// flag.IntVar(&friendMsgSenderNum, "f", 100, "friend msg sender num")
-// flag.IntVar(&NotFriendMsgSenderNum, "n", 100, "not friend msg sender num")
-// flag.IntVar(&groupMsgSenderNum, "g", 100, "group msg sender num")
-// flag.IntVar(&msgSenderNumEvreyUser, "m", 100, "msg sender num evrey user")
-//
-// flag.IntVar(&recvMsgUserNum, "r", 20, "recv msg user num")
-// flag.IntVar(&fastenedUserNum, "u", 300, "fastened user num")
-//}
-//
-//func init() {
-//
-// InitWithFlag()
-//
-// if err := log.InitFromConfig("sdk.log", "sdk", 4,
-// true, false, "./chat_log", 2, 24); err != nil {
-// panic(err)
-// }
-//}
-//
-//func Test_PressureFull(t *testing.T) {
-// flag.Parse()
-// if friendMsgSenderNum+NotFriendMsgSenderNum+groupMsgSenderNum > totalOnlineUserNum {
-// t.Fatal("sender num > total online user num")
-// }
-//
-// p := NewPressureTester()
-// // gen userIDs
-// userIDs, fastenedUserIDs, recvMsgUserIDs := p.genUserIDs()
-//
-// //// register
-// //if err := p.registerUsers(userIDs, fastenedUserIDs, recvMsgUserIDs); err != nil {
-// // t.Fatalf("register users failed, err: %v", err)
-// //}
-// // init users
-// p.initUserConns(userIDs, fastenedUserIDs)
-//
-// // create groups
-// err := p.createTestGroups(userIDs, fastenedUserIDs, recvMsgUserIDs)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // import friends
-// if err := p.importFriends(p.friendSenderUserIDs, fastenedUserIDs); err != nil {
-// t.Fatal(err)
-// }
-//
-// p.pressureSendMsg()
-// // send msg test
-//}
-//
-//func Test_InitUserConn(t *testing.T) {
-// flag.Parse()
-// p := NewPressureTester()
-// userNum := 50000
-// // gen userIDs
-// userIDs := p.userManager.GenUserIDs(userNum)
-// // register
-// //if err := p.registerUsers(userIDs, nil, nil); err != nil {
-// // t.Fatalf("register users failed, err: %v", err)
-// //}
-// // init users
-// p.initUserConns(userIDs, nil)
-// time.Sleep(time.Hour * 60)
-//}
diff --git a/msgtest/sdk_user_simulator/listener.go b/msgtest/sdk_user_simulator/listener.go
index 3b9eef811..96d364c51 100644
--- a/msgtest/sdk_user_simulator/listener.go
+++ b/msgtest/sdk_user_simulator/listener.go
@@ -86,7 +86,7 @@ func (m *MsgListenerCallBak) OnRecvNewMessage(message string) {
case constant.SingleChatType:
m.SingleDelay[sm.SendID] =
append(m.SingleDelay[sm.SendID], &SingleMessage{SendID: sm.SendID, ClientMsgID: sm.ClientMsgID, Delay: GetRelativeServerTime() - sm.SendTime})
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
m.GroupDelay[sm.GroupID] =
append(m.GroupDelay[sm.GroupID], &SingleMessage{SendID: sm.SendID, ClientMsgID: sm.ClientMsgID, Delay: GetRelativeServerTime() - sm.SendTime})
default:
@@ -98,6 +98,7 @@ func (m *MsgListenerCallBak) OnRecvC2CReadReceipt(msgReceiptList string) {
}
func (m *MsgListenerCallBak) OnMsgDeleted(s string) {}
+func (m *MsgListenerCallBak) OnMsgEdited(s string) {}
func (m *MsgListenerCallBak) OnRecvOfflineNewMessage(message string) {
}
diff --git a/msgtest/sdk_user_simulator/user.go b/msgtest/sdk_user_simulator/user.go
index 6e5f33812..8ff706dd7 100644
--- a/msgtest/sdk_user_simulator/user.go
+++ b/msgtest/sdk_user_simulator/user.go
@@ -6,10 +6,10 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/openimsdk/openim-sdk-core/v3/version"
+ "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/log"
)
@@ -47,10 +47,10 @@ func InitSDKAndLogin(userID, token string) error {
cf.LogFilePath = ""
var testConnListener testConnListener
userForSDK.InitSDK(cf, &testConnListener)
- if err := log.InitFromConfig(userID+"_open-im-sdk-core", "", int(LogLevel), true, false, cf.DataDir, 0, 24, version.Version); err != nil {
+ if err := log.InitLoggerFromConfig(userID+"_open-im-sdk-core", "", cf.SystemType, constant.PlatformID2Name[int(cf.PlatformID)], int(LogLevel), true, false, cf.DataDir, 0, 24, version.Version, false); err != nil {
return err
}
- ctx := ccontext.WithOperationID(userForSDK.BaseCtx(), utils.OperationIDGenerator())
+ ctx := ccontext.WithOperationID(userForSDK.Context(), utils.OperationIDGenerator())
SetListener(userForSDK, userID)
err := userForSDK.Login(ctx, userID, token)
if err != nil {
@@ -72,7 +72,7 @@ func SetListener(userForSDK *open_im_sdk.LoginMgr, userID string) {
userForSDK.SetAdvancedMsgListener(msgCallBack)
var friendListener testFriendListener
- userForSDK.SetFriendListener(friendListener)
+ userForSDK.SetFriendshipListener(friendListener)
var groupListener testGroupListener
userForSDK.SetGroupListener(groupListener)
diff --git a/open_im_sdk/caller.go b/open_im_sdk/caller.go
index 484aa080f..f2b41e9ff 100644
--- a/open_im_sdk/caller.go
+++ b/open_im_sdk/caller.go
@@ -17,17 +17,20 @@ package open_im_sdk
import (
"context"
"encoding/json"
- "errors"
"fmt"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"reflect"
"runtime"
"runtime/debug"
"time"
+ "github.com/pkg/errors"
+
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
+
"github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mw"
"github.com/openimsdk/tools/errs"
)
@@ -96,32 +99,37 @@ func call_(operationID string, fn any, args ...any) (res any, err error) {
if err := CheckResourceLoad(UserForSDK, funcName); err != nil {
return nil, sdkerrs.ErrResourceLoad.WrapMsg("not load resource")
}
- ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
+ ctx := ccontext.WithOperationID(UserForSDK.Context(), operationID)
+
defer func(start time.Time) {
if r := recover(); r != nil {
- fmt.Printf("panic: %+v\n%s", r, debug.Stack())
- err = fmt.Errorf("call panic: %+v", r)
+ p := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+ err = fmt.Errorf("call panic: %+v", p)
} else {
elapsed := time.Since(start).Milliseconds()
if err == nil {
- log.ZInfo(ctx, "fn call success", "function name", funcName, "resp", res, "cost time", fmt.Sprintf("%d ms", elapsed))
+ log.ZInfo(ctx, "fn call success", "function name", funcName, "cost time", fmt.Sprintf("%d ms", elapsed), "resp", res)
} else {
- log.ZError(ctx, "fn call error", err, "function name", funcName, "cost time", fmt.Sprintf("%d ms", elapsed))
+ log.ZError(ctx, "fn call error", mw.FormatError(err), "function name", funcName, "cost time", fmt.Sprintf("%d ms", elapsed))
}
}
}(t)
+
log.ZInfo(ctx, "func call req", "function name", funcName, "args", args)
fnv := reflect.ValueOf(fn)
if fnv.Kind() != reflect.Func {
return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("call function fn is not function, is %T", fn))
}
+
fnt := fnv.Type()
nin := fnt.NumIn()
+
if len(args)+1 != nin {
- return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("go code error: fn in args num is not match"))
+ return nil, sdkerrs.ErrSdkInternal.WrapMsg("go code error: fn in args num is not match")
}
+
ins := make([]reflect.Value, 0, nin)
ins = append(ins, reflect.ValueOf(ctx))
for i := 0; i < len(args); i++ {
@@ -156,18 +164,22 @@ func call_(operationID string, fn any, args ...any) (res any, err error) {
continue
}
}
+
//if isNumeric(arg.Kind()) && isNumeric(inFnField.Kind()) {
// v := reflect.Zero(inFnField).Interface()
// setNumeric(args[i], &v)
// ins = append(ins, reflect.ValueOf(v))
// continue
//}
- return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("go code error: fn in args type is not match"))
+
+ return nil, sdkerrs.ErrSdkInternal.WrapMsg("go code error: fn in args type is not match")
}
+
outs := fnv.Call(ins)
if len(outs) == 0 {
return "", nil
}
+
if fnt.Out(len(outs) - 1).Implements(reflect.ValueOf(new(error)).Elem().Type()) {
if errValueOf := outs[len(outs)-1]; !errValueOf.IsNil() {
return nil, errValueOf.Interface().(error)
@@ -177,6 +189,7 @@ func call_(operationID string, fn any, args ...any) (res any, err error) {
}
outs = outs[:len(outs)-1]
}
+
for i := 0; i < len(outs); i++ {
out := outs[i]
switch out.Kind() {
@@ -190,13 +203,16 @@ func call_(operationID string, fn any, args ...any) (res any, err error) {
}
}
}
+
if len(outs) == 1 {
return outs[0].Interface(), nil
}
+
val := make([]any, 0, len(outs))
for i := range outs {
val = append(val, outs[i].Interface())
}
+
return val, nil
}
@@ -208,8 +224,8 @@ func call(callback open_im_sdk_callback.Base, operationID string, fn any, args .
go func() {
res, err := call_(operationID, fn, args...)
if err != nil {
- if code, ok := err.(errs.CodeError); ok {
- callback.OnError(int32(code.Code()), code.Error())
+ if code, ok := errs.Unwrap(err).(errs.CodeError); ok {
+ callback.OnError(int32(code.Code()), err.Error())
} else {
callback.OnError(sdkerrs.UnknownCode, fmt.Sprintf("error %T not implement CodeError: %s", err, err))
}
@@ -247,7 +263,7 @@ func syncCall(operationID string, fn any, args ...any) (res string) {
}
ins := make([]reflect.Value, 0, numIn)
- ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
+ ctx := ccontext.WithOperationID(UserForSDK.Context(), operationID)
t := time.Now()
defer func(start time.Time) {
if r := recover(); r != nil {
@@ -314,12 +330,11 @@ func syncCall(operationID string, fn any, args ...any) (res string) {
return ""
}
if len(outs) == 1 {
- //callback.OnSuccess("") // 只有一个返回值为error,且error == nil
return ""
}
outVals = outVals[:len(outVals)-1]
}
- // 将map和slice的nil转换为非nil
+ // Convert nil maps and slices to non-nil
for i := 0; i < len(outVals); i++ {
switch outs[i].Kind() {
case reflect.Map:
@@ -382,7 +397,7 @@ func messageCall_(callback open_im_sdk_callback.SendMsgCallBack, operationID str
t := time.Now()
ins := make([]reflect.Value, 0, numIn)
- ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
+ ctx := ccontext.WithOperationID(UserForSDK.Context(), operationID)
ctx = ccontext.WithSendMessageCallback(ctx, callback)
funcPtr := reflect.ValueOf(fn).Pointer()
funcName := runtime.FuncForPC(funcPtr).Name()
@@ -430,8 +445,8 @@ func messageCall_(callback open_im_sdk_callback.SendMsgCallBack, operationID str
}
if lastErr {
if last := outVals[len(outVals)-1]; last != nil {
- if code, ok := last.(error).(errs.CodeError); ok {
- callback.OnError(int32(code.Code()), code.Error())
+ if code, ok := errs.Unwrap(last.(error)).(errs.CodeError); ok {
+ callback.OnError(int32(code.Code()), last.(error).Error())
} else {
callback.OnError(sdkerrs.UnknownCode, fmt.Sprintf("error %T not implement CodeError: %s", last.(error), last.(error).Error()))
}
@@ -440,7 +455,7 @@ func messageCall_(callback open_im_sdk_callback.SendMsgCallBack, operationID str
outVals = outVals[:len(outVals)-1]
}
- // 将map和slice的nil转换为非nil
+ // Convert nil maps and slices to non-nil
for i := 0; i < len(outVals); i++ {
switch outs[i].Kind() {
case reflect.Map:
diff --git a/open_im_sdk/conversation_msg.go b/open_im_sdk/conversation_msg.go
index 48512635f..57ef9a50d 100644
--- a/open_im_sdk/conversation_msg.go
+++ b/open_im_sdk/conversation_msg.go
@@ -15,6 +15,8 @@
package open_im_sdk
import (
+ "context"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
)
@@ -34,49 +36,18 @@ func GetMultipleConversation(callback open_im_sdk_callback.Base, operationID str
call(callback, operationID, UserForSDK.Conversation().GetMultipleConversation, conversationIDList)
}
-func SetConversationMsgDestructTime(callback open_im_sdk_callback.Base, operationID string, conversationID string, msgDestructTime int64) {
- call(callback, operationID, UserForSDK.Conversation().SetConversationMsgDestructTime, conversationID, msgDestructTime)
+func SetConversation(callback open_im_sdk_callback.Base, operationID string, conversationID string, req string) {
+ call(callback, operationID, UserForSDK.Conversation().SetConversation, conversationID, req)
}
-func SetConversationIsMsgDestruct(callback open_im_sdk_callback.Base, operationID string, conversationID string, isMsgDestruct bool) {
- call(callback, operationID, UserForSDK.Conversation().SetConversationIsMsgDestruct, conversationID, isMsgDestruct)
-}
-func SetConversationEx(callback open_im_sdk_callback.Base, operationID string, conversationID string, ex string) {
- call(callback, operationID, UserForSDK.Conversation().SetOneConversationEx, conversationID, ex)
-}
func HideConversation(callback open_im_sdk_callback.Base, operationID string, conversationID string) {
call(callback, operationID, UserForSDK.Conversation().HideConversation, conversationID)
}
-// deprecated
-func GetConversationRecvMessageOpt(callback open_im_sdk_callback.Base, operationID string, conversationIDList string) {
- call(callback, operationID, UserForSDK.Conversation().GetConversationRecvMessageOpt, conversationIDList)
-}
-
func SetConversationDraft(callback open_im_sdk_callback.Base, operationID string, conversationID string, draftText string) {
call(callback, operationID, UserForSDK.Conversation().SetConversationDraft, conversationID, draftText)
}
-func ResetConversationGroupAtType(callback open_im_sdk_callback.Base, operationID string, conversationID string) {
- call(callback, operationID, UserForSDK.Conversation().ResetConversationGroupAtType, conversationID)
-}
-
-func PinConversation(callback open_im_sdk_callback.Base, operationID string, conversationID string, isPinned bool) {
- call(callback, operationID, UserForSDK.Conversation().PinConversation, conversationID, isPinned)
-}
-
-func SetConversationPrivateChat(callback open_im_sdk_callback.Base, operationID string, conversationID string, isPrivate bool) {
- call(callback, operationID, UserForSDK.Conversation().SetOneConversationPrivateChat, conversationID, isPrivate)
-}
-
-func SetConversationBurnDuration(callback open_im_sdk_callback.Base, operationID string, conversationID string, duration int32) {
- call(callback, operationID, UserForSDK.Conversation().SetOneConversationBurnDuration, conversationID, duration)
-}
-
-func SetConversationRecvMessageOpt(callback open_im_sdk_callback.Base, operationID string, conversationID string, opt int) {
- call(callback, operationID, UserForSDK.Conversation().SetOneConversationRecvMessageOpt, conversationID, opt)
-}
-
func GetTotalUnreadMsgCount(callback open_im_sdk_callback.Base, operationID string) {
call(callback, operationID, UserForSDK.Conversation().GetTotalUnreadMsgCount)
}
@@ -110,42 +81,17 @@ func CreateCardMessage(operationID string, cardInfo string) string {
return syncCall(operationID, UserForSDK.Conversation().CreateCardMessage, cardInfo)
}
-func CreateVideoMessageFromFullPath(operationID string, videoFullPath string, videoType string, duration int64, snapshotFullPath string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateVideoMessageFromFullPath, videoFullPath, videoType, duration, snapshotFullPath)
-}
-func CreateImageMessageFromFullPath(operationID string, imageFullPath string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateImageMessageFromFullPath, imageFullPath)
-}
-func CreateSoundMessageFromFullPath(operationID string, soundPath string, duration int64) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateSoundMessageFromFullPath, soundPath, duration)
-}
-func CreateFileMessageFromFullPath(operationID string, fileFullPath, fileName string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateFileMessageFromFullPath, fileFullPath, fileName)
+func CreateImageMessage(operationID string, imageSourcePath string, sourcePicture, bigPicture, snapshotPicture string) string {
+ return syncCall(operationID, UserForSDK.Conversation().CreateImageMessage, imageSourcePath, sourcePicture, bigPicture, snapshotPicture)
}
-func CreateImageMessage(operationID string, imagePath string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateImageMessage, imagePath)
-}
-func CreateImageMessageByURL(operationID string, sourcePath string, sourcePicture, bigPicture, snapshotPicture string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateImageMessageByURL, sourcePath, sourcePicture, bigPicture, snapshotPicture)
-}
-
-func CreateSoundMessageByURL(operationID string, soundBaseInfo string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateSoundMessageByURL, soundBaseInfo)
+func CreateSoundMessage(operationID string, soundPath string, duration int64, soundBaseInfo string) string {
+ return syncCall(operationID, UserForSDK.Conversation().CreateSoundMessage, soundPath, duration, soundBaseInfo)
}
-func CreateSoundMessage(operationID string, soundPath string, duration int64) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateSoundMessage, soundPath, duration)
+func CreateVideoMessage(operationID string, videoSourcePath string, videoType string, duration int64, snapshotSourcePath string, videoBaseInfo string) string {
+ return syncCall(operationID, UserForSDK.Conversation().CreateVideoMessage, videoSourcePath, videoType, duration, snapshotSourcePath, videoBaseInfo)
}
-func CreateVideoMessageByURL(operationID string, videoBaseInfo string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateVideoMessageByURL, videoBaseInfo)
-}
-func CreateVideoMessage(operationID string, videoPath string, videoType string, duration int64, snapshotPath string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateVideoMessage, videoPath, videoType, duration, snapshotPath)
-}
-func CreateFileMessageByURL(operationID string, fileBaseInfo string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateFileMessageByURL, fileBaseInfo)
-}
-func CreateFileMessage(operationID string, filePath string, fileName string) string {
- return syncCall(operationID, UserForSDK.Conversation().CreateFileMessage, filePath, fileName)
+func CreateFileMessage(operationID string, fileSourcePath string, fileName string, fileBaseInfo string) string {
+ return syncCall(operationID, UserForSDK.Conversation().CreateFileMessage, fileSourcePath, fileName, fileBaseInfo)
}
func CreateMergerMessage(operationID string, messageList, title, summaryList string) string {
return syncCall(operationID, UserForSDK.Conversation().CreateMergerMessage, messageList, title, summaryList)
@@ -157,14 +103,11 @@ func CreateForwardMessage(operationID string, m string) string {
return syncCall(operationID, UserForSDK.Conversation().CreateForwardMessage, m)
}
func GetConversationIDBySessionType(operationID string, sourceID string, sessionType int) string {
- return syncCall(operationID, UserForSDK.Conversation().GetConversationIDBySessionType, sourceID, sessionType)
+ return UserForSDK.Conversation().GetConversationIDBySessionType(context.Background(), sourceID, sessionType)
}
func SendMessage(callback open_im_sdk_callback.SendMsgCallBack, operationID, message, recvID, groupID, offlinePushInfo string, isOnlineOnly bool) {
messageCall(callback, operationID, UserForSDK.Conversation().SendMessage, message, recvID, groupID, offlinePushInfo, isOnlineOnly)
}
-func SendMessageNotOss(callback open_im_sdk_callback.SendMsgCallBack, operationID string, message, recvID, groupID string, offlinePushInfo string, isOnlineOnly bool) {
- messageCall(callback, operationID, UserForSDK.Conversation().SendMessageNotOss, message, recvID, groupID, offlinePushInfo, isOnlineOnly)
-}
func FindMessageList(callback open_im_sdk_callback.Base, operationID string, findMessageOptions string) {
call(callback, operationID, UserForSDK.Conversation().FindMessageList, findMessageOptions)
@@ -191,6 +134,10 @@ func MarkConversationMessageAsRead(callback open_im_sdk_callback.Base, operation
call(callback, operationID, UserForSDK.Conversation().MarkConversationMessageAsRead, conversationID)
}
+func MarkAllConversationMessageAsRead(callback open_im_sdk_callback.Base, operationID string) {
+ call(callback, operationID, UserForSDK.Conversation().MarkAllConversationMessageAsRead)
+}
+
func MarkMessagesAsReadByMsgID(callback open_im_sdk_callback.Base, operationID string, conversationID string, clientMsgIDs string) {
call(callback, operationID, UserForSDK.Conversation().MarkMessagesAsReadByMsgID, conversationID, clientMsgIDs)
}
@@ -208,7 +155,7 @@ func HideAllConversations(callback open_im_sdk_callback.Base, operationID string
}
func DeleteAllMsgFromLocalAndSvr(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.Conversation().DeleteAllMsgFromLocalAndSvr)
+ call(callback, operationID, UserForSDK.Conversation().DeleteAllMsgFromLocalAndServer)
}
func DeleteAllMsgFromLocal(callback open_im_sdk_callback.Base, operationID string) {
diff --git a/open_im_sdk/em.go b/open_im_sdk/em.go
index e7f0e7f8b..42116d7a4 100644
--- a/open_im_sdk/em.go
+++ b/open_im_sdk/em.go
@@ -17,6 +17,7 @@ package open_im_sdk
import (
"context"
"errors"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/tools/log"
)
@@ -195,7 +196,7 @@ func (e *emptyAdvancedMsgListener) OnRecvOnlineOnlyMessage(message string) {
}
func (e *emptyAdvancedMsgListener) OnRecvNewMessage(message string) {
- log.ZWarn(e.ctx, "AdvancedMsgListener is not implemented", nil, "message", message)
+ log.ZWarn(e.ctx, "AdvancedMsgListener is not implemented OnRecvNewMessage", nil, "message", message)
}
func (e *emptyAdvancedMsgListener) OnRecvC2CReadReceipt(msgReceiptList string) {
@@ -213,6 +214,10 @@ func (e *emptyAdvancedMsgListener) OnNewRecvMessageRevoked(messageRevoked string
log.ZWarn(e.ctx, "AdvancedMsgListener is not implemented", nil, "messageRevoked", messageRevoked)
}
+func (e *emptyAdvancedMsgListener) OnMsgEdited(msg string) {
+ log.ZWarn(e.ctx, "OnMsgEdited is not implemented", nil, "msg", msg)
+}
+
func (e *emptyAdvancedMsgListener) OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string) {
log.ZWarn(e.ctx, "AdvancedMsgListener is not implemented", nil, "msgID", msgID,
"reactionExtensionList", reactionExtensionList)
@@ -236,20 +241,6 @@ func (e *emptyAdvancedMsgListener) OnMsgDeleted(message string) {
log.ZWarn(e.ctx, "AdvancedMsgListener is not implemented", nil, "message", message)
}
-type emptyBatchMsgListener struct{}
-
-func newEmptyBatchMsgListener() *emptyBatchMsgListener {
- return &emptyBatchMsgListener{}
-}
-
-func (e *emptyBatchMsgListener) OnRecvNewMessages(messageList string) {
-
-}
-
-func (e *emptyBatchMsgListener) OnRecvOfflineNewMessages(messageList string) {
-
-}
-
type emptyUserListener struct {
ctx context.Context
}
diff --git a/open_im_sdk/file.go b/open_im_sdk/file.go
deleted file mode 100644
index ad7e9a7d5..000000000
--- a/open_im_sdk/file.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package open_im_sdk
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
-)
-
-func UploadFile(callback open_im_sdk_callback.Base, operationID string, req string, progress open_im_sdk_callback.UploadFileCallback) {
- call(callback, operationID, UserForSDK.File().UploadFile, req, file.UploadFileCallback(progress))
-}
diff --git a/open_im_sdk/friend.go b/open_im_sdk/friend.go
deleted file mode 100644
index a9d07841d..000000000
--- a/open_im_sdk/friend.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package open_im_sdk
-
-import "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
-
-func GetSpecifiedFriendsInfo(callback open_im_sdk_callback.Base, operationID string, userIDList string) {
- call(callback, operationID, UserForSDK.Friend().GetSpecifiedFriendsInfo, userIDList)
-}
-
-func GetFriendList(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.Friend().GetFriendList)
-}
-
-func GetFriendListPage(callback open_im_sdk_callback.Base, operationID string, offset int32, count int32) {
- call(callback, operationID, UserForSDK.Friend().GetFriendListPage, offset, count)
-}
-
-// func GetFriendListPageV2(callback open_im_sdk_callback.Base, operationID string, offset int32, count int32) {
-// call(callback, operationID, UserForSDK.Friend().GetFriendListPageV2, offset, count)
-// }
-
-func SearchFriends(callback open_im_sdk_callback.Base, operationID string, searchParam string) {
- call(callback, operationID, UserForSDK.Friend().SearchFriends, searchParam)
-}
-
-func CheckFriend(callback open_im_sdk_callback.Base, operationID string, userIDList string) {
- call(callback, operationID, UserForSDK.Friend().CheckFriend, userIDList)
-}
-
-func AddFriend(callback open_im_sdk_callback.Base, operationID string, userIDReqMsg string) {
- call(callback, operationID, UserForSDK.Friend().AddFriend, userIDReqMsg)
-}
-
-func SetFriendRemark(callback open_im_sdk_callback.Base, operationID string, userIDRemark string) {
- call(callback, operationID, UserForSDK.Friend().SetFriendRemark, userIDRemark)
-}
-func PinFriends(callback open_im_sdk_callback.Base, operationID string, pinFriendsParams string) {
- call(callback, operationID, UserForSDK.Friend().PinFriends, pinFriendsParams)
-}
-func DeleteFriend(callback open_im_sdk_callback.Base, operationID string, friendUserID string) {
- call(callback, operationID, UserForSDK.Friend().DeleteFriend, friendUserID)
-}
-
-func GetFriendApplicationListAsRecipient(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.Friend().GetFriendApplicationListAsRecipient)
-}
-
-func GetFriendApplicationListAsApplicant(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.Friend().GetFriendApplicationListAsApplicant)
-}
-
-func AcceptFriendApplication(callback open_im_sdk_callback.Base, operationID string, userIDHandleMsg string) {
- call(callback, operationID, UserForSDK.Friend().AcceptFriendApplication, userIDHandleMsg)
-}
-
-func RefuseFriendApplication(callback open_im_sdk_callback.Base, operationID string, userIDHandleMsg string) {
- call(callback, operationID, UserForSDK.Friend().RefuseFriendApplication, userIDHandleMsg)
-}
-
-func AddBlack(callback open_im_sdk_callback.Base, operationID string, blackUserID string, ex string) {
- call(callback, operationID, UserForSDK.Friend().AddBlack, blackUserID, ex)
-}
-
-func GetBlackList(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.Friend().GetBlackList)
-}
-
-func RemoveBlack(callback open_im_sdk_callback.Base, operationID string, removeUserID string) {
- call(callback, operationID, UserForSDK.Friend().RemoveBlack, removeUserID)
-}
-func SetFriendsEx(callback open_im_sdk_callback.Base, operationID string, friendIDs string, ex string) {
- call(callback, operationID, UserForSDK.Friend().SetFriendsEx, friendIDs, ex)
-}
diff --git a/open_im_sdk/group.go b/open_im_sdk/group.go
index 1db3528dd..6aca35bbe 100644
--- a/open_im_sdk/group.go
+++ b/open_im_sdk/group.go
@@ -16,10 +16,6 @@ package open_im_sdk
import "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
-//funcation CreateGroup(callback open_im_sdk_callback.Base, operationID string, groupBaseInfo string, memberList string) {
-// call(callback, operationID, UserForSDK.Group().CreateGroup, groupBaseInfo, memberList)
-//}
-
func CreateGroup(callback open_im_sdk_callback.Base, operationID string, groupReqInfo string) {
call(callback, operationID, UserForSDK.Group().CreateGroup, groupReqInfo)
}
@@ -36,17 +32,17 @@ func DismissGroup(callback open_im_sdk_callback.Base, operationID string, groupI
call(callback, operationID, UserForSDK.Group().DismissGroup, groupID)
}
-func SetGroupVerification(callback open_im_sdk_callback.Base, operationID string, groupID string, verification int32) {
- call(callback, operationID, UserForSDK.Group().SetGroupVerification, groupID, verification)
-}
-
-func SetGroupApplyMemberFriend(callback open_im_sdk_callback.Base, operationID string, groupID string, rule int32) {
- call(callback, operationID, UserForSDK.Group().SetGroupApplyMemberFriend, groupID, rule)
-}
-
-func SetGroupLookMemberInfo(callback open_im_sdk_callback.Base, operationID string, groupID string, rule int32) {
- call(callback, operationID, UserForSDK.Group().SetGroupLookMemberInfo, groupID, rule)
-}
+//func SetGroupVerification(callback open_im_sdk_callback.Base, operationID string, groupID string, verification int32) {
+// call(callback, operationID, UserForSDK.Group().SetGroupVerification, groupID, verification)
+//}
+//
+//func SetGroupApplyMemberFriend(callback open_im_sdk_callback.Base, operationID string, groupID string, rule int32) {
+// call(callback, operationID, UserForSDK.Group().SetGroupApplyMemberFriend, groupID, rule)
+//}
+//
+//func SetGroupLookMemberInfo(callback open_im_sdk_callback.Base, operationID string, groupID string, rule int32) {
+// call(callback, operationID, UserForSDK.Group().SetGroupLookMemberInfo, groupID, rule)
+//}
func ChangeGroupMute(callback open_im_sdk_callback.Base, operationID string, groupID string, isMute bool) {
call(callback, operationID, UserForSDK.Group().ChangeGroupMute, groupID, isMute)
@@ -72,13 +68,13 @@ func SetGroupMemberInfo(callback open_im_sdk_callback.Base, operationID string,
call(callback, operationID, UserForSDK.Group().SetGroupMemberInfo, groupMemberInfo)
}
-func SetGroupMemberRoleLevel(callback open_im_sdk_callback.Base, operationID string, groupID string, userID string, roleLevel int) {
- call(callback, operationID, UserForSDK.Group().SetGroupMemberRoleLevel, groupID, userID, roleLevel)
-}
-
-func SetGroupMemberNickname(callback open_im_sdk_callback.Base, operationID string, groupID string, userID string, groupMemberNickname string) {
- call(callback, operationID, UserForSDK.Group().SetGroupMemberNickname, groupID, userID, groupMemberNickname)
-}
+//func SetGroupMemberRoleLevel(callback open_im_sdk_callback.Base, operationID string, groupID string, userID string, roleLevel int) {
+// call(callback, operationID, UserForSDK.Group().SetGroupMemberRoleLevel, groupID, userID, roleLevel)
+//}
+//
+//func SetGroupMemberNickname(callback open_im_sdk_callback.Base, operationID string, groupID string, userID string, groupMemberNickname string) {
+// call(callback, operationID, UserForSDK.Group().SetGroupMemberNickname, groupID, userID, groupMemberNickname)
+//}
func GetJoinedGroupList(callback open_im_sdk_callback.Base, operationID string) {
call(callback, operationID, UserForSDK.Group().GetJoinedGroupList)
@@ -88,10 +84,6 @@ func GetJoinedGroupListPage(callback open_im_sdk_callback.Base, operationID stri
call(callback, operationID, UserForSDK.Group().GetJoinedGroupListPage, offset, count)
}
-// func GetJoinedGroupListPageV2(callback open_im_sdk_callback.Base, operationID string, offset, count int32) {
-// call(callback, operationID, UserForSDK.Group().GetJoinedGroupListPageV2, offset, count)
-// }
-
func GetSpecifiedGroupsInfo(callback open_im_sdk_callback.Base, operationID string, groupIDList string) {
call(callback, operationID, UserForSDK.Group().GetSpecifiedGroupsInfo, groupIDList)
}
@@ -108,10 +100,6 @@ func GetGroupMemberListByJoinTimeFilter(callback open_im_sdk_callback.Base, oper
call(callback, operationID, UserForSDK.Group().GetGroupMemberListByJoinTimeFilter, groupID, offset, count, joinTimeBegin, joinTimeEnd, filterUserIDList)
}
-// func GetGroupMemberListByJoinTimeFilterV2(callback open_im_sdk_callback.Base, operationID string, groupID string, offset int32, count int32, joinTimeBegin int64, joinTimeEnd int64, filterUserIDList string) {
-// call(callback, operationID, UserForSDK.Group().GetGroupMemberListByJoinTimeFilterV2, groupID, offset, count, joinTimeBegin, joinTimeEnd, filterUserIDList)
-// }
-
func GetSpecifiedGroupMembersInfo(callback open_im_sdk_callback.Base, operationID string, groupID string, userIDList string) {
call(callback, operationID, UserForSDK.Group().GetSpecifiedGroupMembersInfo, groupID, userIDList)
}
@@ -120,10 +108,6 @@ func GetGroupMemberList(callback open_im_sdk_callback.Base, operationID string,
call(callback, operationID, UserForSDK.Group().GetGroupMemberList, groupID, filter, offset, count)
}
-// func GetGroupMemberListV2(callback open_im_sdk_callback.Base, operationID string, groupID string, filter int32, offset int32, count int32) {
-// call(callback, operationID, UserForSDK.Group().GetGroupMemberListV2, groupID, filter, offset, count)
-// }
-
func GetGroupApplicationListAsRecipient(callback open_im_sdk_callback.Base, operationID string) {
call(callback, operationID, UserForSDK.Group().GetGroupApplicationListAsRecipient)
}
diff --git a/open_im_sdk/init_login.go b/open_im_sdk/init_login.go
index bdb0e353b..32db2b5da 100644
--- a/open_im_sdk/init_login.go
+++ b/open_im_sdk/init_login.go
@@ -22,6 +22,8 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ pbConstant "github.com/openimsdk/protocol/constant"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/openimsdk/openim-sdk-core/v3/version"
@@ -31,18 +33,14 @@ import (
)
func GetSdkVersion() string {
- return constant.GetSdkVersion()
+ return version.Version
}
const (
- rotateCount uint = 0
+ rotateCount uint = 1
rotationTime uint = 24
)
-func SetHeartbeatInterval(heartbeatInterval int) {
- constant.HeartbeatInterval = heartbeatInterval
-}
-
func InitSDK(listener open_im_sdk_callback.OnConnListener, operationID string, config string) bool {
if UserForSDK != nil {
fmt.Println(operationID, "Initialize multiple times, use the existing ", UserForSDK, " Previous configuration ", UserForSDK.ImConfig(), " now configuration: ", config)
@@ -56,7 +54,7 @@ func InitSDK(listener open_im_sdk_callback.OnConnListener, operationID string, c
if configArgs.PlatformID == 0 {
return false
}
- if err := log.InitFromConfig("open-im-sdk-core", "", int(configArgs.LogLevel), configArgs.IsLogStandardOutput, false, configArgs.LogFilePath, rotateCount, rotationTime, version.Version); err != nil {
+ if err := log.InitLoggerFromConfig("open-im-sdk-core", "", configArgs.SystemType, pbConstant.PlatformID2Name[int(configArgs.PlatformID)], int(configArgs.LogLevel), configArgs.IsLogStandardOutput, false, configArgs.LogFilePath, rotateCount, rotationTime, version.Version, true); err != nil {
fmt.Println(operationID, "log init failed ", err.Error())
}
fmt.Println("init log success")
@@ -71,7 +69,7 @@ func InitSDK(listener open_im_sdk_callback.OnConnListener, operationID string, c
return false
}
- log.ZInfo(ctx, "InitSDK info", "config", configArgs, "sdkVersion", GetSdkVersion())
+ log.ZInfo(ctx, "InitSDK info", "config", configArgs)
if listener == nil || config == "" {
log.ZError(ctx, "listener or config is nil", nil)
return false
diff --git a/open_im_sdk/listener.go b/open_im_sdk/listener.go
index c244ad9ca..c9f4a9711 100644
--- a/open_im_sdk/listener.go
+++ b/open_im_sdk/listener.go
@@ -30,17 +30,13 @@ func SetAdvancedMsgListener(listener open_im_sdk_callback.OnAdvancedMsgListener)
listenerCall(UserForSDK.SetAdvancedMsgListener, listener)
}
-func SetBatchMsgListener(listener open_im_sdk_callback.OnBatchMsgListener) {
- listenerCall(UserForSDK.SetBatchMsgListener, listener)
-}
-
func SetUserListener(listener open_im_sdk_callback.OnUserListener) {
listenerCall(UserForSDK.SetUserListener, listener)
}
func SetFriendListener(listener open_im_sdk_callback.OnFriendshipListener) {
- listenerCall(UserForSDK.SetFriendListener, listener)
+ listenerCall(UserForSDK.SetFriendshipListener, listener)
}
func SetCustomBusinessListener(listener open_im_sdk_callback.OnCustomBusinessListener) {
diff --git a/open_im_sdk/online.go b/open_im_sdk/online.go
new file mode 100644
index 000000000..3b371126b
--- /dev/null
+++ b/open_im_sdk/online.go
@@ -0,0 +1,25 @@
+package open_im_sdk
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+)
+
+// SubscribeUsersStatus Presence status of subscribed users.
+func SubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
+ call(callback, operationID, UserForSDK.LongConnMgr().SubscribeUsersStatus, userIDs)
+}
+
+// UnsubscribeUsersStatus Unsubscribe a user's presence.
+func UnsubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
+ call(callback, operationID, UserForSDK.LongConnMgr().UnsubscribeUsersStatus, userIDs)
+}
+
+// GetSubscribeUsersStatus Get the online status of subscribers.
+func GetSubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string) {
+ call(callback, operationID, UserForSDK.LongConnMgr().GetSubscribeUsersStatus)
+}
+
+// GetUserStatus Get the online status of users.
+func GetUserStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
+ call(callback, operationID, UserForSDK.LongConnMgr().SubscribeUsersStatus, userIDs)
+}
diff --git a/open_im_sdk/relation.go b/open_im_sdk/relation.go
new file mode 100644
index 000000000..c68b14850
--- /dev/null
+++ b/open_im_sdk/relation.go
@@ -0,0 +1,88 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package open_im_sdk
+
+import "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+
+func GetSpecifiedFriendsInfo(callback open_im_sdk_callback.Base, operationID string, userIDList string, filterBlack bool) {
+ call(callback, operationID, UserForSDK.Relation().GetSpecifiedFriendsInfo, userIDList, filterBlack)
+}
+
+func GetFriendList(callback open_im_sdk_callback.Base, operationID string, filterBlack bool) {
+ call(callback, operationID, UserForSDK.Relation().GetFriendList, filterBlack)
+}
+
+func GetFriendListPage(callback open_im_sdk_callback.Base, operationID string, offset int32, count int32, filterBlack bool) {
+ call(callback, operationID, UserForSDK.Relation().GetFriendListPage, offset, count, filterBlack)
+}
+
+func SearchFriends(callback open_im_sdk_callback.Base, operationID string, searchParam string) {
+ call(callback, operationID, UserForSDK.Relation().SearchFriends, searchParam)
+}
+
+func CheckFriend(callback open_im_sdk_callback.Base, operationID string, userIDList string) {
+ call(callback, operationID, UserForSDK.Relation().CheckFriend, userIDList)
+}
+
+func AddFriend(callback open_im_sdk_callback.Base, operationID string, userIDReqMsg string) {
+ call(callback, operationID, UserForSDK.Relation().AddFriend, userIDReqMsg)
+}
+
+func UpdateFriends(callback open_im_sdk_callback.Base, operationID string, req string) {
+ call(callback, operationID, UserForSDK.Relation().UpdateFriends, req)
+}
+
+//func SetFriendRemark(callback open_im_sdk_callback.Base, operationID string, userIDRemark string) {
+// call(callback, operationID, UserForSDK.Relation().SetFriendRemark, userIDRemark)
+//}
+//func PinFriends(callback open_im_sdk_callback.Base, operationID string, pinFriendsParams string) {
+// call(callback, operationID, UserForSDK.Relation().PinFriends, pinFriendsParams)
+//}
+
+func DeleteFriend(callback open_im_sdk_callback.Base, operationID string, friendUserID string) {
+ call(callback, operationID, UserForSDK.Relation().DeleteFriend, friendUserID)
+}
+
+func GetFriendApplicationListAsRecipient(callback open_im_sdk_callback.Base, operationID string) {
+ call(callback, operationID, UserForSDK.Relation().GetFriendApplicationListAsRecipient)
+}
+
+func GetFriendApplicationListAsApplicant(callback open_im_sdk_callback.Base, operationID string) {
+ call(callback, operationID, UserForSDK.Relation().GetFriendApplicationListAsApplicant)
+}
+
+func AcceptFriendApplication(callback open_im_sdk_callback.Base, operationID string, userIDHandleMsg string) {
+ call(callback, operationID, UserForSDK.Relation().AcceptFriendApplication, userIDHandleMsg)
+}
+
+func RefuseFriendApplication(callback open_im_sdk_callback.Base, operationID string, userIDHandleMsg string) {
+ call(callback, operationID, UserForSDK.Relation().RefuseFriendApplication, userIDHandleMsg)
+}
+
+func AddBlack(callback open_im_sdk_callback.Base, operationID string, blackUserID string, ex string) {
+ call(callback, operationID, UserForSDK.Relation().AddBlack, blackUserID, ex)
+}
+
+func GetBlackList(callback open_im_sdk_callback.Base, operationID string) {
+ call(callback, operationID, UserForSDK.Relation().GetBlackList)
+}
+
+func RemoveBlack(callback open_im_sdk_callback.Base, operationID string, removeUserID string) {
+ call(callback, operationID, UserForSDK.Relation().RemoveBlack, removeUserID)
+}
+
+//func SetFriendsEx(callback open_im_sdk_callback.Base, operationID string, friendIDs string, ex string) {
+// call(callback, operationID, UserForSDK.Relation().SetFriendsEx, friendIDs, ex)
+//}
diff --git a/open_im_sdk/third.go b/open_im_sdk/third.go
index 492270538..94f575982 100644
--- a/open_im_sdk/third.go
+++ b/open_im_sdk/third.go
@@ -15,8 +15,9 @@
package open_im_sdk
import (
- "github.com/openimsdk/openim-sdk-core/v3/internal/third"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
)
func UpdateFcmToken(callback open_im_sdk_callback.Base, operationID, fcmToken string, expireTime int64) {
@@ -27,6 +28,18 @@ func SetAppBadge(callback open_im_sdk_callback.Base, operationID string, appUnre
call(callback, operationID, UserForSDK.Third().SetAppBadge, appUnreadCount)
}
-func UploadLogs(callback open_im_sdk_callback.Base, operationID string, progress open_im_sdk_callback.UploadLogProgress) {
- call(callback, operationID, UserForSDK.Third().UploadLogs, third.Progress(progress))
+func UploadLogs(callback open_im_sdk_callback.Base, operationID string, line int, ex string, progress open_im_sdk_callback.UploadLogProgress) {
+ call(callback, operationID, UserForSDK.Third().UploadLogs, line, ex, progress)
+}
+
+func Logs(callback open_im_sdk_callback.Base, operationID string, logLevel int, file string, line int, msgs string, err string, keyAndValue string) {
+ if UserForSDK == nil || UserForSDK.Third() == nil {
+ callback.OnError(sdkerrs.SdkInternalError, "sdk not init")
+ return
+ }
+ call(callback, operationID, UserForSDK.Third().Log, logLevel, file, line, msgs, err, keyAndValue)
+}
+
+func UploadFile(callback open_im_sdk_callback.Base, operationID string, req string, progress open_im_sdk_callback.UploadFileCallback) {
+ call(callback, operationID, UserForSDK.File().UploadFile, req, file.UploadFileCallback(progress))
}
diff --git a/open_im_sdk/user.go b/open_im_sdk/user.go
index 247378a55..1b9d293f6 100644
--- a/open_im_sdk/user.go
+++ b/open_im_sdk/user.go
@@ -19,62 +19,24 @@ import (
)
func GetUsersInfo(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
- call(callback, operationID, UserForSDK.Full().GetUsersInfo, userIDs)
-}
-
-func GetUsersInfoWithCache(callback open_im_sdk_callback.Base, operationID string, userIDs, groupID string) {
- call(callback, operationID, UserForSDK.Full().GetUsersInfoWithCache, userIDs, groupID)
-}
-
-// GetUsersInfoFromSrv obtains the information about multiple users.
-func GetUsersInfoFromSrv(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
call(callback, operationID, UserForSDK.User().GetUsersInfo, userIDs)
}
// SetSelfInfo sets the user's own information.
-// Deprecated: user SetSelfInfoEx instead
func SetSelfInfo(callback open_im_sdk_callback.Base, operationID string, userInfo string) {
call(callback, operationID, UserForSDK.User().SetSelfInfo, userInfo)
}
-// SetSelfInfoEx sets the user's own information with Ex field.
-func SetSelfInfoEx(callback open_im_sdk_callback.Base, operationID string, userInfo string) {
- call(callback, operationID, UserForSDK.User().SetSelfInfoEx, userInfo)
-}
-func SetGlobalRecvMessageOpt(callback open_im_sdk_callback.Base, operationID string, opt int) {
- call(callback, operationID, UserForSDK.User().SetGlobalRecvMessageOpt, opt)
-}
+//// SetSelfInfo sets the user's own information with Ex field.
+//func SetSelfInfo(callback open_im_sdk_callback.Base, operationID string, userInfo string) {
+// call(callback, operationID, UserForSDK.User().SetSelfInfo, userInfo)
+//}
// GetSelfUserInfo obtains the user's own information.
func GetSelfUserInfo(callback open_im_sdk_callback.Base, operationID string) {
call(callback, operationID, UserForSDK.User().GetSelfUserInfo)
}
-// UpdateMsgSenderInfo updates the message sender's nickname and face URL.
-func UpdateMsgSenderInfo(callback open_im_sdk_callback.Base, operationID string, nickname, faceURL string) {
- call(callback, operationID, UserForSDK.User().UpdateMsgSenderInfo, nickname, faceURL)
-}
-
-// SubscribeUsersStatus Presence status of subscribed users.
-func SubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
- call(callback, operationID, UserForSDK.User().SubscribeUsersStatus, userIDs)
-}
-
-// UnsubscribeUsersStatus Unsubscribe a user's presence.
-func UnsubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
- call(callback, operationID, UserForSDK.User().UnsubscribeUsersStatus, userIDs)
-}
-
-// GetSubscribeUsersStatus Get the online status of subscribers.
-func GetSubscribeUsersStatus(callback open_im_sdk_callback.Base, operationID string) {
- call(callback, operationID, UserForSDK.User().GetSubscribeUsersStatus)
-}
-
-// GetUserStatus Get the online status of users.
-func GetUserStatus(callback open_im_sdk_callback.Base, operationID string, userIDs string) {
- call(callback, operationID, UserForSDK.User().GetUserStatus, userIDs)
-}
-
// AddUserCommand add to user's favorite
func AddUserCommand(callback open_im_sdk_callback.Base, operationID string, Type int32, uuid string, value string) {
call(callback, operationID, UserForSDK.User().ProcessUserCommandAdd, Type, uuid, value)
diff --git a/open_im_sdk/userRelated.go b/open_im_sdk/userRelated.go
index de1bc0814..ce452c604 100644
--- a/open_im_sdk/userRelated.go
+++ b/open_im_sdk/userRelated.go
@@ -17,18 +17,19 @@ package open_im_sdk
import (
"context"
"encoding/json"
- "errors"
"fmt"
+ "runtime/debug"
"strings"
"sync"
"time"
"unsafe"
- "github.com/openimsdk/openim-sdk-core/v3/internal/business"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/flagconst"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
+
+ "github.com/openimsdk/openim-sdk-core/v3/internal/relation"
+
conv "github.com/openimsdk/openim-sdk-core/v3/internal/conversation_msg"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
- "github.com/openimsdk/openim-sdk-core/v3/internal/friend"
- "github.com/openimsdk/openim-sdk-core/v3/internal/full"
"github.com/openimsdk/openim-sdk-core/v3/internal/group"
"github.com/openimsdk/openim-sdk-core/v3/internal/interaction"
"github.com/openimsdk/openim-sdk-core/v3/internal/third"
@@ -41,11 +42,12 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/openimsdk/protocol/push"
"github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/jsonutil"
)
const (
@@ -66,7 +68,7 @@ var (
// CheckResourceLoad checks the SDK is resource load status.
func CheckResourceLoad(uSDK *LoginMgr, funcName string) error {
if uSDK == nil {
- return utils.Wrap(errors.New("CheckResourceLoad failed uSDK == nil "), "")
+ return errs.New("SDK not initialized,userForSDK is nil", "funcName", funcName).Wrap()
}
if funcName == "" {
return nil
@@ -75,22 +77,19 @@ func CheckResourceLoad(uSDK *LoginMgr, funcName string) error {
if parts[len(parts)-1] == "Login-fm" {
return nil
}
- if uSDK.Friend() == nil || uSDK.User() == nil || uSDK.Group() == nil || uSDK.Conversation() == nil ||
- uSDK.Full() == nil {
- return utils.Wrap(errors.New("CheckResourceLoad failed, resource nil "), "")
+ if uSDK.getLoginStatus(context.Background()) != Logged {
+ return errs.New("SDK not logged in", "funcName", funcName).Wrap()
}
return nil
}
type LoginMgr struct {
- friend *friend.Friend
+ relation *relation.Relation
group *group.Group
conversation *conv.Conversation
user *user.User
file *file.File
- business *business.Business
- full *full.Full
db db_interface.DataBase
longConnMgr *interaction.LongConnMgr
msgSyncer *interaction.MsgSyncer
@@ -105,20 +104,18 @@ type LoginMgr struct {
loginStatus int
groupListener open_im_sdk_callback.OnGroupListener
- friendListener open_im_sdk_callback.OnFriendshipListener
+ friendshipListener open_im_sdk_callback.OnFriendshipListener
conversationListener open_im_sdk_callback.OnConversationListener
advancedMsgListener open_im_sdk_callback.OnAdvancedMsgListener
- batchMsgListener open_im_sdk_callback.OnBatchMsgListener
userListener open_im_sdk_callback.OnUserListener
signalingListener open_im_sdk_callback.OnSignalingListener
businessListener open_im_sdk_callback.OnCustomBusinessListener
msgKvListener open_im_sdk_callback.OnMessageKvInfoListener
- conversationCh chan common.Cmd2Value
- cmdWsCh chan common.Cmd2Value
- heartbeatCmdCh chan common.Cmd2Value
- pushMsgAndMaxSeqCh chan common.Cmd2Value
- loginMgrCh chan common.Cmd2Value
+ conversationCh chan common.Cmd2Value
+ cmdWsCh chan common.Cmd2Value
+ msgSyncerCh chan common.Cmd2Value
+ loginMgrCh chan common.Cmd2Value
ctx context.Context
cancel context.CancelFunc
@@ -130,8 +127,8 @@ func (u *LoginMgr) GroupListener() open_im_sdk_callback.OnGroupListener {
return u.groupListener
}
-func (u *LoginMgr) FriendListener() open_im_sdk_callback.OnFriendshipListener {
- return u.friendListener
+func (u *LoginMgr) FriendshipListener() open_im_sdk_callback.OnFriendshipListener {
+ return u.friendshipListener
}
func (u *LoginMgr) ConversationListener() open_im_sdk_callback.OnConversationListener {
@@ -142,10 +139,6 @@ func (u *LoginMgr) AdvancedMsgListener() open_im_sdk_callback.OnAdvancedMsgListe
return u.advancedMsgListener
}
-func (u *LoginMgr) BatchMsgListener() open_im_sdk_callback.OnBatchMsgListener {
- return u.batchMsgListener
-}
-
func (u *LoginMgr) UserListener() open_im_sdk_callback.OnUserListener {
return u.userListener
}
@@ -162,10 +155,6 @@ func (u *LoginMgr) MsgKvListener() open_im_sdk_callback.OnMessageKvInfoListener
return u.msgKvListener
}
-func (u *LoginMgr) BaseCtx() context.Context {
- return u.ctx
-}
-
func (u *LoginMgr) Exit() {
u.cancel()
}
@@ -201,16 +190,12 @@ func (u *LoginMgr) File() *file.File {
return u.file
}
-func (u *LoginMgr) Full() *full.Full {
- return u.full
-}
-
func (u *LoginMgr) Group() *group.Group {
return u.group
}
-func (u *LoginMgr) Friend() *friend.Friend {
- return u.friend
+func (u *LoginMgr) Relation() *relation.Relation {
+ return u.relation
}
func (u *LoginMgr) SetConversationListener(conversationListener open_im_sdk_callback.OnConversationListener) {
@@ -225,12 +210,8 @@ func (u *LoginMgr) SetMessageKvInfoListener(messageKvInfoListener open_im_sdk_ca
u.msgKvListener = messageKvInfoListener
}
-func (u *LoginMgr) SetBatchMsgListener(batchMsgListener open_im_sdk_callback.OnBatchMsgListener) {
- u.batchMsgListener = batchMsgListener
-}
-
-func (u *LoginMgr) SetFriendListener(friendListener open_im_sdk_callback.OnFriendshipListener) {
- u.friendListener = friendListener
+func (u *LoginMgr) SetFriendshipListener(friendshipListener open_im_sdk_callback.OnFriendshipListener) {
+ u.friendshipListener = friendshipListener
}
func (u *LoginMgr) SetGroupListener(groupListener open_im_sdk_callback.OnGroupListener) {
@@ -248,6 +229,14 @@ func (u *LoginMgr) GetLoginUserID() string {
return u.loginUserID
}
func (u *LoginMgr) logoutListener(ctx context.Context) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+
+ log.ZWarn(ctx, "logoutListener panic", nil, "panic info", err)
+ }
+ }()
+
for {
select {
case <-u.loginMgrCh:
@@ -266,7 +255,7 @@ func (u *LoginMgr) logoutListener(ctx context.Context) {
func NewLoginMgr() *LoginMgr {
return &LoginMgr{
- info: &ccontext.GlobalConfig{}, // 分配内存空间
+ info: &ccontext.GlobalConfig{},
}
}
func (u *LoginMgr) getLoginStatus(_ context.Context) int {
@@ -316,7 +305,7 @@ func (u *LoginMgr) handlerSendingMsg(ctx context.Context, sendingMsg *model_stru
}
if latestMsg.ClientMsgID == sendingMsg.ClientMsgID {
latestMsg.Status = constant.MsgStatusSendFailed
- conversation.LatestMsg = utils.StructToJsonString(latestMsg)
+ conversation.LatestMsg = jsonutil.StructToJsonString(latestMsg)
return u.db.UpdateConversation(ctx, conversation)
}
return nil
@@ -327,10 +316,11 @@ func (u *LoginMgr) login(ctx context.Context, userID, token string) error {
return sdkerrs.ErrLoginRepeat
}
u.setLoginStatus(Logging)
- u.info.UserID = userID
- u.info.Token = token
log.ZDebug(ctx, "login start... ", "userID", userID, "token", token)
t1 := time.Now()
+
+ u.info.UserID = userID
+ u.info.Token = token
u.token = token
u.loginUserID = userID
var err error
@@ -342,18 +332,17 @@ func (u *LoginMgr) login(ctx context.Context, userID, token string) error {
log.ZDebug(ctx, "NewDataBase ok", "userID", userID, "dataDir", u.info.DataDir, "login cost time", time.Since(t1))
u.user = user.NewUser(u.db, u.loginUserID, u.conversationCh)
u.file = file.NewFile(u.db, u.loginUserID)
- u.friend = friend.NewFriend(u.loginUserID, u.db, u.user, u.conversationCh)
+ u.relation = relation.NewFriend(u.loginUserID, u.db, u.user, u.conversationCh)
u.group = group.NewGroup(u.loginUserID, u.db, u.conversationCh)
- u.full = full.NewFull(u.user, u.friend, u.group, u.conversationCh, u.db)
- u.business = business.NewBusiness(u.db)
- u.third = third.NewThird(u.info.PlatformID, u.loginUserID, constant.SdkVersion, u.info.SystemType, u.info.LogFilePath, u.file)
+ u.third = third.NewThird(u.info.PlatformID, u.loginUserID, u.info.SystemType, u.info.LogFilePath, u.file)
log.ZDebug(ctx, "forcedSynchronization success...", "login cost time: ", time.Since(t1))
- u.msgSyncer, _ = interaction.NewMsgSyncer(ctx, u.conversationCh, u.pushMsgAndMaxSeqCh, u.loginUserID, u.longConnMgr, u.db, 0)
- u.conversation = conv.NewConversation(ctx, u.longConnMgr, u.db, u.conversationCh,
- u.friend, u.group, u.user, u.business, u.full, u.file)
+ u.msgSyncer, _ = interaction.NewMsgSyncer(ctx, u.conversationCh, u.msgSyncerCh, u.loginUserID, u.longConnMgr, u.db, 0)
+ u.conversation = conv.NewConversation(ctx, u.longConnMgr, u.db, u.conversationCh, u.msgSyncerCh,
+ u.relation, u.group, u.user, u.file)
u.setListener(ctx)
+
u.run(ctx)
u.setLoginStatus(Logged)
log.ZDebug(ctx, "login success...", "login cost time: ", time.Since(t1))
@@ -362,12 +351,11 @@ func (u *LoginMgr) login(ctx context.Context, userID, token string) error {
func (u *LoginMgr) setListener(ctx context.Context) {
setListener(ctx, &u.userListener, u.UserListener, u.user.SetListener, newEmptyUserListener)
- setListener(ctx, &u.friendListener, u.FriendListener, u.friend.SetListener, newEmptyFriendshipListener)
+ setListener(ctx, &u.friendshipListener, u.FriendshipListener, u.relation.SetListener, newEmptyFriendshipListener)
setListener(ctx, &u.groupListener, u.GroupListener, u.group.SetGroupListener, newEmptyGroupListener)
setListener(ctx, &u.conversationListener, u.ConversationListener, u.conversation.SetConversationListener, newEmptyConversationListener)
setListener(ctx, &u.advancedMsgListener, u.AdvancedMsgListener, u.conversation.SetMsgListener, newEmptyAdvancedMsgListener)
- setListener(ctx, &u.batchMsgListener, u.BatchMsgListener, u.conversation.SetBatchMsgListener, nil)
- setListener(ctx, &u.businessListener, u.BusinessListener, u.business.SetListener, newEmptyCustomBusinessListener)
+ setListener(ctx, &u.businessListener, u.BusinessListener, u.conversation.SetBusinessListener, newEmptyCustomBusinessListener)
}
func setListener[T any](ctx context.Context, listener *T, getter func() T, setFunc func(listener func() T), newFunc func(context.Context) T) {
@@ -380,7 +368,7 @@ func setListener[T any](ctx context.Context, listener *T, getter func() T, setFu
func (u *LoginMgr) run(ctx context.Context) {
u.longConnMgr.Run(ctx)
go u.msgSyncer.DoListener(ctx)
- go common.DoListener(u.conversation, u.ctx)
+ go common.DoListener(u.ctx, u.conversation)
go u.logoutListener(ctx)
}
@@ -402,15 +390,24 @@ func (u *LoginMgr) Context() context.Context {
func (u *LoginMgr) initResources() {
ctx := ccontext.WithInfo(context.Background(), u.info)
u.ctx, u.cancel = context.WithCancel(ctx)
- u.conversationCh = make(chan common.Cmd2Value, 1000)
- u.heartbeatCmdCh = make(chan common.Cmd2Value, 10)
- u.pushMsgAndMaxSeqCh = make(chan common.Cmd2Value, 1000)
+ var convChanLen int
+ if flagconst.TestMode {
+ convChanLen = 100000
+ } else {
+ convChanLen = 1000
+ }
+ u.conversationCh = make(chan common.Cmd2Value, convChanLen)
+ u.msgSyncerCh = make(chan common.Cmd2Value, 1000)
u.loginMgrCh = make(chan common.Cmd2Value, 1)
- u.longConnMgr = interaction.NewLongConnMgr(u.ctx, u.connListener, u.heartbeatCmdCh, u.pushMsgAndMaxSeqCh, u.loginMgrCh)
+ u.longConnMgr = interaction.NewLongConnMgr(u.ctx, u.connListener, u.userOnlineStatusChange, u.msgSyncerCh, u.loginMgrCh)
u.ctx = ccontext.WithApiErrCode(u.ctx, &apiErrCallback{loginMgrCh: u.loginMgrCh, listener: u.connListener})
u.setLoginStatus(LogoutStatus)
}
+func (u *LoginMgr) userOnlineStatusChange(users map[string][]int32) {
+ u.User().UserOnlineStatusChange(users)
+}
+
func (u *LoginMgr) UnInitSDK() {
if u.getLoginStatus(context.Background()) == Logged {
fmt.Println("sdk not logout, please logout first")
@@ -458,10 +455,14 @@ func (u *LoginMgr) setAppBackgroundStatus(ctx context.Context, isBackground bool
return err
} else {
u.longConnMgr.SetBackground(isBackground)
- if isBackground == false {
- _ = common.TriggerCmdWakeUp(u.heartbeatCmdCh)
- _ = common.TriggerCmdSyncData(u.conversationCh)
+ if !isBackground {
+ _ = common.TriggerCmdWakeUpDataSync(ctx, u.msgSyncerCh)
}
+
return nil
}
}
+
+func (u *LoginMgr) LongConnMgr() *interaction.LongConnMgr {
+ return u.longConnMgr
+}
diff --git a/open_im_sdk_callback/callback_client.go b/open_im_sdk_callback/callback_client.go
index 0a003ce4b..671c20539 100644
--- a/open_im_sdk_callback/callback_client.go
+++ b/open_im_sdk_callback/callback_client.go
@@ -70,20 +70,11 @@ type OnConversationListener interface {
type OnAdvancedMsgListener interface {
OnRecvNewMessage(message string)
OnRecvC2CReadReceipt(msgReceiptList string)
- OnRecvGroupReadReceipt(groupMsgReceiptList string)
-
OnNewRecvMessageRevoked(messageRevoked string)
- OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string)
- OnRecvMessageExtensionsDeleted(msgID string, reactionExtensionKeyList string)
- OnRecvMessageExtensionsAdded(msgID string, reactionExtensionList string)
OnRecvOfflineNewMessage(message string)
OnMsgDeleted(message string)
OnRecvOnlineOnlyMessage(message string)
-}
-
-type OnBatchMsgListener interface {
- OnRecvNewMessages(messageList string)
- OnRecvOfflineNewMessages(messageList string)
+ OnMsgEdited(message string)
}
type OnUserListener interface {
@@ -102,15 +93,15 @@ type OnMessageKvInfoListener interface {
}
type OnListenerForService interface {
- //有人申请进群
+ // OnGroupApplicationAdded Someone applied to join a group
OnGroupApplicationAdded(groupApplication string)
- //进群申请被同意
+ // OnGroupApplicationAccepted Group join application has been accepted
OnGroupApplicationAccepted(groupApplication string)
- //有人申请添加你为好友
+ // OnFriendApplicationAdded Someone applied to add you as a friend
OnFriendApplicationAdded(friendApplication string)
- //好友申请被同意
- OnFriendApplicationAccepted(groupApplication string)
- //收到新消息
+ // OnFriendApplicationAccepted Friend request has been accepted
+ OnFriendApplicationAccepted(friendApplication string)
+ // OnRecvNewMessage Received a new message
OnRecvNewMessage(message string)
}
@@ -124,11 +115,11 @@ type OnSignalingListener interface {
OnInviteeRejected(inviteeRejectedCallback string)
OnInviteeRejectedByOtherDevice(inviteeRejectedCallback string)
- //
+
OnInvitationCancelled(invitationCancelledCallback string)
- //
+
OnInvitationTimeout(invitationTimeoutCallback string)
- //
+
OnHangUp(hangUpCallback string)
OnRoomParticipantConnected(onRoomParticipantConnectedCallback string)
@@ -137,22 +128,24 @@ type OnSignalingListener interface {
}
type UploadFileCallback interface {
- Open(size int64) // 文件打开的大小
- PartSize(partSize int64, num int) // 分片大小,数量
- HashPartProgress(index int, size int64, partHash string) // 每块分片的hash值
- HashPartComplete(partsHash string, fileHash string) // 分块完成,服务端标记hash和文件最终hash
- UploadID(uploadID string) // 上传ID
- UploadPartComplete(index int, partSize int64, partHash string) // 上传分片进度
- UploadComplete(fileSize int64, streamSize int64, storageSize int64) // 整体进度
- Complete(size int64, url string, typ int) // 上传完成
+ // Open a file with a given size
+ Open(size int64)
+ // PartSize Set the size of each part and the total number of parts
+ PartSize(partSize int64, num int)
+ // HashPartProgress Progress of hashing each part, including the part index, size, and hash value
+ HashPartProgress(index int, size int64, partHash string)
+ // HashPartComplete All parts have been hashed, providing the combined hash of all parts and the final file hash
+ HashPartComplete(partsHash string, fileHash string)
+ // UploadID Upload ID is generated and provided
+ UploadID(uploadID string)
+ // UploadPartComplete A specific part has completed uploading, providing the part index, size, and hash value
+ UploadPartComplete(index int, partSize int64, partHash string)
+ // UploadComplete The entire file upload progress, including the file size, stream size, and storage size
+ UploadComplete(fileSize int64, streamSize int64, storageSize int64)
+ // Complete The file upload is complete, providing the final size, URL, and type of the file
+ Complete(size int64, url string, typ int)
}
type UploadLogProgress interface {
OnProgress(current int64, size int64)
}
-
-type AppDataSyncListener interface {
- OnAppDataSyncStart()
- OnAppDataSyncProgress(progress int)
- OnAppDataSyncFinish()
-}
diff --git a/pkg/api/api.go b/pkg/api/api.go
new file mode 100644
index 000000000..3c6a85c8b
--- /dev/null
+++ b/pkg/api/api.go
@@ -0,0 +1,115 @@
+package api
+
+import (
+ "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/relation"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/protocol/user"
+)
+
+var (
+ ParseToken = newApi[auth.ParseTokenReq, auth.ParseTokenResp]("/auth/parse_token")
+)
+
+var (
+ GetUsersInfo = newApi[user.GetDesignateUsersReq, user.GetDesignateUsersResp]("/user/get_users_info")
+ UpdateUserInfo = newApi[user.UpdateUserInfoReq, user.UpdateUserInfoResp]("/user/update_user_info")
+ UpdateUserInfoEx = newApi[user.UpdateUserInfoExReq, user.UpdateUserInfoExResp]("/user/update_user_info_ex")
+ ProcessUserCommandAdd = newApi[user.ProcessUserCommandAddReq, user.ProcessUserCommandAddResp]("/user/process_user_command_add")
+ ProcessUserCommandDelete = newApi[user.ProcessUserCommandDeleteReq, user.ProcessUserCommandDeleteResp]("/user/process_user_command_delete")
+ ProcessUserCommandUpdate = newApi[user.ProcessUserCommandUpdateReq, user.ProcessUserCommandUpdateResp]("/user/process_user_command_update")
+ ProcessUserCommandGet = newApi[user.ProcessUserCommandGetReq, user.ProcessUserCommandGetResp]("/user/process_user_command_get")
+ ProcessUserCommandGetAll = newApi[user.ProcessUserCommandGetAllReq, user.ProcessUserCommandGetAllResp]("/user/process_user_command_get_all")
+ UserRegister = newApi[user.UserRegisterReq, user.UserRegisterResp]("/user/user_register")
+)
+
+var (
+ AddFriend = newApi[relation.ApplyToAddFriendReq, relation.ApplyToAddFriendResp]("/friend/add_friend")
+ DeleteFriend = newApi[relation.DeleteFriendReq, relation.DeleteFriendResp]("/friend/delete_friend")
+ GetFriendApplicationList = newApi[relation.GetPaginationFriendsApplyToReq, relation.GetPaginationFriendsApplyToResp]("/friend/get_friend_apply_list")
+ GetSelfFriendApplicationList = newApi[relation.GetPaginationFriendsApplyFromReq, relation.GetPaginationFriendsApplyFromResp]("/friend/get_self_friend_apply_list")
+ ImportFriendList = newApi[relation.ImportFriendReq, relation.ImportFriendResp]("/friend/import_friend")
+ GetDesignatedFriendsApply = newApi[relation.GetDesignatedFriendsApplyReq, relation.GetDesignatedFriendsApplyResp]("/friend/get_designated_friend_apply")
+ GetFriendList = newApi[relation.GetPaginationFriendsReq, relation.GetPaginationFriendsResp]("/friend/get_friend_list")
+ GetDesignatedFriends = newApi[relation.GetDesignatedFriendsReq, relation.GetDesignatedFriendsResp]("/friend/get_designated_friends")
+ AddFriendResponse = newApi[relation.RespondFriendApplyReq, relation.RespondFriendApplyResp]("/friend/add_friend_response")
+ SetFriendRemark = newApi[relation.SetFriendRemarkReq, relation.SetFriendRemarkResp]("/friend/set_friend_remark")
+ UpdateFriends = newApi[relation.UpdateFriendsReq, relation.UpdateFriendsResp]("/friend/update_friends")
+ GetIncrementalFriends = newApi[relation.GetIncrementalFriendsReq, relation.GetIncrementalFriendsResp]("/friend/get_incremental_friends")
+ GetFullFriendUserIDs = newApi[relation.GetFullFriendUserIDsReq, relation.GetFullFriendUserIDsResp]("/friend/get_full_friend_user_ids")
+ AddBlack = newApi[relation.AddBlackReq, relation.AddBlackResp]("/friend/add_black")
+ RemoveBlack = newApi[relation.RemoveBlackReq, relation.RemoveBlackResp]("/friend/remove_black")
+ GetBlackList = newApi[relation.GetPaginationBlacksReq, relation.GetPaginationBlacksResp]("/friend/get_black_list")
+)
+
+var (
+ ClearConversationMsg = newApi[msg.ClearConversationsMsgReq, msg.ClearConversationsMsgResp]("/msg/clear_conversation_msg") // Clear the message of the specified conversation
+ ClearAllMsg = newApi[msg.UserClearAllMsgReq, msg.UserClearAllMsgResp]("/msg/user_clear_all_msg") // Clear all messages of the current user
+ DeleteMsgs = newApi[msg.DeleteMsgsReq, msg.DeleteMsgsResp]("/msg/delete_msgs") // Delete the specified message
+ RevokeMsg = newApi[msg.RevokeMsgReq, msg.RevokeMsgResp]("/msg/revoke_msg")
+ MarkMsgsAsRead = newApi[msg.MarkMsgsAsReadReq, msg.MarkMsgsAsReadResp]("/msg/mark_msgs_as_read")
+ GetConversationsHasReadAndMaxSeq = newApi[msg.GetConversationsHasReadAndMaxSeqReq, msg.GetConversationsHasReadAndMaxSeqResp]("/msg/get_conversations_has_read_and_max_seq")
+ MarkConversationAsRead = newApi[msg.MarkConversationAsReadReq, msg.MarkConversationAsReadResp]("/msg/mark_conversation_as_read")
+ SetConversationHasReadSeq = newApi[msg.SetConversationHasReadSeqReq, msg.SetConversationHasReadSeqResp]("/msg/set_conversation_has_read_seq")
+ SendMsg = newApi[msg.SendMsgReq, msg.SendMsgResp]("/msg/send_msg")
+ GetServerTime = newApi[msg.GetServerTimeReq, msg.GetServerTimeResp]("/msg/get_server_time")
+ GetStreamMsg = newApi[msg.GetStreamMsgReq, msg.GetStreamMsgResp]("/msg/get_stream_msg")
+)
+
+var (
+ CreateGroup = newApi[group.CreateGroupReq, group.CreateGroupResp]("/group/create_group")
+ SetGroupInfoEx = newApi[group.SetGroupInfoExReq, group.SetGroupInfoExResp]("/group/set_group_info_ex")
+ JoinGroup = newApi[group.JoinGroupReq, group.JoinGroupResp]("/group/join_group")
+ QuitGroup = newApi[group.QuitGroupReq, group.QuitGroupResp]("/group/quit_group")
+ GetGroupsInfo = newApi[group.GetGroupsInfoReq, group.GetGroupsInfoResp]("/group/get_groups_info")
+ GetGroupMemberList = newApi[group.GetGroupMemberListReq, group.GetGroupMemberListResp]("/group/get_group_member_list")
+ GetGroupMembersInfo = newApi[group.GetGroupMembersInfoReq, group.GetGroupMembersInfoResp]("/group/get_group_members_info")
+ InviteUserToGroup = newApi[group.InviteUserToGroupReq, group.InviteUserToGroupResp]("/group/invite_user_to_group")
+ GetJoinedGroupList = newApi[group.GetJoinedGroupListReq, group.GetJoinedGroupListResp]("/group/get_joined_group_list")
+ KickGroupMember = newApi[group.KickGroupMemberReq, group.KickGroupMemberResp]("/group/kick_group")
+ TransferGroup = newApi[group.TransferGroupOwnerReq, group.TransferGroupOwnerResp]("/group/transfer_group")
+ GetRecvGroupApplicationList = newApi[group.GetGroupApplicationListReq, group.GetGroupApplicationListResp]("/group/get_recv_group_applicationList")
+ GetSendGroupApplicationList = newApi[group.GetUserReqApplicationListReq, group.GetUserReqApplicationListResp]("/group/get_user_req_group_applicationList")
+ AcceptGroupApplication = newApi[group.GroupApplicationResponseReq, group.GroupApplicationResponseResp]("/group/group_application_response")
+ DismissGroup = newApi[group.DismissGroupReq, group.DismissGroupResp]("/group/dismiss_group")
+ MuteGroupMember = newApi[group.MuteGroupMemberReq, group.MuteGroupMemberResp]("/group/mute_group_member")
+ CancelMuteGroupMember = newApi[group.CancelMuteGroupMemberReq, group.CancelMuteGroupMemberResp]("/group/cancel_mute_group_member")
+ MuteGroup = newApi[group.MuteGroupReq, group.MuteGroupResp]("/group/mute_group")
+ CancelMuteGroup = newApi[group.CancelMuteGroupReq, group.CancelMuteGroupResp]("/group/cancel_mute_group")
+ SetGroupMemberInfo = newApi[group.SetGroupMemberInfoReq, group.SetGroupMemberInfoResp]("/group/set_group_member_info")
+ GetIncrementalJoinGroup = newApi[group.GetIncrementalJoinGroupReq, group.GetIncrementalJoinGroupResp]("/group/get_incremental_join_groups")
+ GetIncrementalGroupMemberBatch = newApi[group.BatchGetIncrementalGroupMemberReq, group.BatchGetIncrementalGroupMemberResp]("/group/get_incremental_group_members_batch")
+ GetFullJoinedGroupIDs = newApi[group.GetFullJoinGroupIDsReq, group.GetFullJoinGroupIDsResp]("/group/get_full_join_group_ids")
+ GetFullGroupMemberUserIDs = newApi[group.GetFullGroupMemberUserIDsReq, group.GetFullGroupMemberUserIDsResp]("/group/get_full_group_member_user_ids")
+)
+
+var (
+ GetConversations = newApi[conversation.GetConversationsReq, conversation.GetConversationsResp]("/conversation/get_conversations")
+ GetAllConversations = newApi[conversation.GetAllConversationsReq, conversation.GetAllConversationsResp]("/conversation/get_all_conversations")
+ SetConversations = newApi[conversation.SetConversationsReq, conversation.SetConversationsResp]("/conversation/set_conversations")
+ GetIncrementalConversation = newApi[conversation.GetIncrementalConversationReq, conversation.GetIncrementalConversationResp]("/conversation/get_incremental_conversations")
+ GetFullConversationIDs = newApi[conversation.GetFullOwnerConversationIDsReq, conversation.GetFullOwnerConversationIDsResp]("/conversation/get_full_conversation_ids")
+ GetOwnerConversation = newApi[conversation.GetOwnerConversationReq, conversation.GetOwnerConversationResp]("/conversation/get_owner_conversation")
+)
+
+var (
+ GetAdminToken = newApi[auth.GetAdminTokenReq, auth.GetAdminTokenResp]("/auth/get_admin_token")
+ GetUsersToken = newApi[auth.GetUserTokenReq, auth.GetUserTokenResp]("/auth/get_user_token")
+)
+
+var (
+ FcmUpdateToken = newApi[third.FcmUpdateTokenReq, third.FcmUpdateTokenResp]("/third/fcm_update_token")
+ SetAppBadge = newApi[third.SetAppBadgeReq, third.SetAppBadgeResp]("/third/set_app_badge")
+ UploadLogs = newApi[third.UploadLogsReq, third.UploadLogsResp]("/third/logs/upload")
+)
+
+var (
+ ObjectPartLimit = newApi[third.PartLimitReq, third.PartLimitResp]("/object/part_limit")
+ ObjectInitiateMultipartUpload = newApi[third.InitiateMultipartUploadReq, third.InitiateMultipartUploadResp]("/object/initiate_multipart_upload")
+ ObjectAuthSign = newApi[third.AuthSignReq, third.AuthSignResp]("/object/auth_sign")
+ ObjectCompleteMultipartUpload = newApi[third.CompleteMultipartUploadReq, third.CompleteMultipartUploadResp]("/object/complete_multipart_upload")
+ ObjectAccessURL = newApi[third.AccessURLReq, third.AccessURLResp]("/object/access_url")
+)
diff --git a/pkg/api/fn.go b/pkg/api/fn.go
new file mode 100644
index 000000000..9eb1fe0d3
--- /dev/null
+++ b/pkg/api/fn.go
@@ -0,0 +1,92 @@
+package api
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
+ "github.com/openimsdk/protocol/sdkws"
+)
+
+func newApi[Req, Resp any](api string) Api[Req, Resp] {
+ return Api[Req, Resp]{
+ api: api,
+ }
+}
+
+type Api[Req, Resp any] struct {
+ api string
+}
+
+func (a Api[Req, Resp]) Invoke(ctx context.Context, req *Req) (*Resp, error) {
+ var resp Resp
+ if err := network.ApiPost(ctx, a.api, req, &resp); err != nil {
+ return nil, err
+ }
+ return &resp, nil
+}
+
+func (a Api[Req, Resp]) Execute(ctx context.Context, req *Req) error {
+ _, err := a.Invoke(ctx, req)
+ return err
+}
+
+func (a Api[Req, Resp]) Route() string {
+ return a.api
+}
+
+// ExtractField is a generic function that extracts a field from the response of a given function.
+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) {
+ resp, err := fn(ctx, req)
+ if err != nil {
+ var c C
+ return c, err
+ }
+ return get(resp), nil
+}
+
+type pagination interface {
+ GetPagination() *sdkws.RequestPagination
+}
+
+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) {
+ if req.GetPagination() == nil {
+ vof := reflect.ValueOf(req)
+ for {
+ if vof.Kind() == reflect.Ptr {
+ vof = vof.Elem()
+ } else {
+ break
+ }
+ }
+ if vof.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("request is not a struct")
+ }
+ fof := vof.FieldByName("Pagination")
+ if !fof.IsValid() {
+ return nil, fmt.Errorf("request is not valid Pagination field")
+ }
+ fof.Set(reflect.ValueOf(&sdkws.RequestPagination{}))
+ }
+ if req.GetPagination().PageNumber < 0 {
+ req.GetPagination().PageNumber = 0
+ }
+ if req.GetPagination().ShowNumber <= 0 {
+ req.GetPagination().ShowNumber = 200
+ }
+ var result []Elem
+ for i := int32(0); ; i++ {
+ req.GetPagination().PageNumber = i + 1
+ resp, err := api(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+ elems := fn(resp)
+ result = append(result, elems...)
+ if len(elems) < int(req.GetPagination().ShowNumber) {
+ break
+ }
+ }
+ return result, nil
+}
diff --git a/internal/cache/cache.go b/pkg/cache/cache.go
similarity index 74%
rename from internal/cache/cache.go
rename to pkg/cache/cache.go
index ed719b1e7..465f261e6 100644
--- a/internal/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -2,15 +2,15 @@ package cache
import "sync"
+func NewCache[K comparable, V any]() *Cache[K, V] {
+ return &Cache[K, V]{}
+}
+
// Cache is a Generic sync.Map structure.
type Cache[K comparable, V any] struct {
m sync.Map
}
-func NewCache[K comparable, V any]() *Cache[K, V] {
- return &Cache[K, V]{}
-}
-
// Load returns the value stored in the map for a key, or nil if no value is present.
func (c *Cache[K, V]) Load(key K) (value V, ok bool) {
rawValue, ok := c.m.Load(key)
@@ -25,6 +25,13 @@ func (c *Cache[K, V]) Store(key K, value V) {
c.m.Store(key, value)
}
+// StoreWithFunc stores the value for a key only if the provided condition function returns true.
+func (c *Cache[K, V]) StoreWithFunc(key K, value V, condition func(key K, value V) bool) {
+ if condition(key, value) {
+ c.m.Store(key, value)
+ }
+}
+
// StoreAll sets all value by f's key.
func (c *Cache[K, V]) StoreAll(f func(value V) K, values []V) {
for _, v := range values {
@@ -51,6 +58,16 @@ func (c *Cache[K, V]) DeleteAll() {
})
}
+// DeleteCon deletes the value for a key only if the provided condition function returns true.
+func (c *Cache[K, V]) DeleteCon(condition func(key K, value V) bool) {
+ c.m.Range(func(rawKey, rawValue interface{}) bool {
+ if condition(rawKey.(K), rawValue.(V)) {
+ c.m.Delete(rawKey)
+ }
+ return true // Continue iteration
+ })
+}
+
// RangeAll returns all values in the map.
func (c *Cache[K, V]) RangeAll() (values []V) {
c.m.Range(func(rawKey, rawValue interface{}) bool {
diff --git a/pkg/cache/conversation_seq_cache.go b/pkg/cache/conversation_seq_cache.go
new file mode 100644
index 000000000..eefa88305
--- /dev/null
+++ b/pkg/cache/conversation_seq_cache.go
@@ -0,0 +1,49 @@
+package cache
+
+import (
+ "strings"
+
+ "github.com/openimsdk/tools/utils/stringutil"
+)
+
+const (
+ ViewHistory = iota
+ ViewSearch
+)
+
+type ConversationSeqContextCache struct {
+ *Cache[string, int64]
+}
+
+func NewConversationSeqContextCache() *ConversationSeqContextCache {
+ return &ConversationSeqContextCache{Cache: NewCache[string, int64]()}
+}
+
+func (c ConversationSeqContextCache) Load(conversationID string, viewType int) (int64, bool) {
+ return c.Cache.Load(c.getConversationViewTypeKey(conversationID, viewType))
+
+}
+func (c ConversationSeqContextCache) Delete(conversationID string, viewType int) {
+ c.Cache.Delete(c.getConversationViewTypeKey(conversationID, viewType))
+
+}
+
+func (c ConversationSeqContextCache) Store(conversationID string, viewType int, thisEndSeq int64) {
+ c.Cache.Store(c.getConversationViewTypeKey(conversationID, viewType), thisEndSeq)
+
+}
+
+func (c ConversationSeqContextCache) StoreWithFunc(conversationID string, viewType int, thisEndSeq int64, fn func(key string, value int64) bool) {
+
+ c.Cache.StoreWithFunc(c.getConversationViewTypeKey(conversationID, viewType), thisEndSeq, fn)
+}
+
+func (c ConversationSeqContextCache) getConversationViewTypeKey(conversationID string, viewType int) string {
+ return conversationID + "::viewType::" + stringutil.IntToString(viewType)
+}
+
+func (c ConversationSeqContextCache) DeleteByViewType(viewType int) {
+ c.Cache.DeleteCon(func(key string, value int64) bool {
+ return strings.Contains(key, "::viewType::"+stringutil.IntToString(viewType))
+ })
+}
diff --git a/pkg/cache/user_cache.go b/pkg/cache/user_cache.go
new file mode 100644
index 000000000..464bd85a2
--- /dev/null
+++ b/pkg/cache/user_cache.go
@@ -0,0 +1,129 @@
+package cache
+
+import (
+ "context"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
+ "github.com/openimsdk/tools/utils/datautil"
+)
+
+func NewUserCache[K comparable, V any](
+ getKeyFunc func(value V) K,
+ batchDBFunc func(ctx context.Context, keys []K) ([]V, error),
+ singleDBFunc func(ctx context.Context, keys K) (V, error),
+ queryFunc func(ctx context.Context, keys []K) ([]V, error),
+) *UserCache[K, V] {
+ return &UserCache[K, V]{
+ Cache: NewCache[K, V](),
+ getKeyFunc: getKeyFunc,
+ batchDBFunc: batchDBFunc,
+ singleDBFunc: singleDBFunc,
+ queryFunc: queryFunc,
+ }
+}
+
+type UserCache[K comparable, V any] struct {
+ *Cache[K, V]
+ getKeyFunc func(value V) K
+ batchDBFunc func(ctx context.Context, keys []K) ([]V, error)
+ singleDBFunc func(ctx context.Context, keys K) (V, error)
+ queryFunc func(ctx context.Context, keys []K) ([]V, error)
+}
+
+func (m *UserCache[K, V]) BatchFetch(ctx context.Context, keys []K) (map[K]V, error) {
+ var (
+ res = make(map[K]V)
+ queryKeys []K
+ )
+
+ for _, key := range keys {
+ if data, ok := m.Load(key); ok {
+ res[key] = data
+ } else {
+ queryKeys = append(queryKeys, keys...)
+ }
+ }
+
+ writeData, err := m.batchFetch(ctx, queryKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ for i, data := range writeData {
+ res[m.getKeyFunc(data)] = writeData[i]
+ m.Store(m.getKeyFunc(data), writeData[i])
+ }
+
+ return res, nil
+}
+
+func (m *UserCache[K, V]) Fetch(ctx context.Context, key K) (V, error) {
+ var nilData V
+
+ if data, ok := m.Load(key); ok {
+ return data, nil
+ }
+
+ fetchedData, err := m.fetch(ctx, key)
+ if err != nil {
+ return nilData, err
+ }
+ m.Store(key, fetchedData)
+ return fetchedData, nil
+}
+
+func (m *UserCache[K, V]) batchFetch(ctx context.Context, keys []K) ([]V, error) {
+ if len(keys) == 0 {
+ return nil, nil
+ }
+ var (
+ queryKeys = keys
+ writeData []V
+ )
+
+ if m.batchDBFunc != nil {
+ dbData, err := m.batchDBFunc(ctx, queryKeys)
+ if err != nil {
+ return nil, err
+ }
+ writeData = dbData
+ queryKeys = datautil.SliceSubAny(queryKeys, dbData, m.getKeyFunc)
+ }
+
+ if len(queryKeys) == 0 {
+ return writeData, nil
+ }
+
+ if m.queryFunc != nil {
+ queryData, err := m.queryFunc(ctx, queryKeys)
+ if err != nil {
+ return nil, err
+ }
+ if len(queryData) == 0 {
+ return writeData, sdkerrs.ErrUserIDNotFound.WrapMsg("fetch data not found", "keys", keys)
+ }
+ writeData = append(writeData, queryData...)
+ }
+
+ return writeData, nil
+}
+func (m *UserCache[K, V]) fetch(ctx context.Context, key K) (V, error) {
+ var writeData V
+ if m.singleDBFunc != nil {
+ dbData, err := m.singleDBFunc(ctx, key)
+ if err == nil {
+ return dbData, nil
+ }
+ }
+ if m.queryFunc != nil {
+ queryData, err := m.queryFunc(ctx, []K{key})
+ if err != nil {
+ return writeData, err
+ }
+ if len(queryData) > 0 {
+ return queryData[0], nil
+ }
+ return writeData, sdkerrs.ErrUserIDNotFound.WrapMsg("fetch data not found", "key", key)
+ }
+ return writeData, nil
+}
diff --git a/pkg/ccontext/context.go b/pkg/ccontext/context.go
index 67855a596..8e83304cd 100644
--- a/pkg/ccontext/context.go
+++ b/pkg/ccontext/context.go
@@ -23,8 +23,14 @@ import (
"github.com/openimsdk/tools/mcontext"
)
+type ctxKey string
+
+const (
+ CtxCallback ctxKey = "callback"
+)
+
const (
- Callback = "callback"
+ CtxApiToken ctxKey = "api-token"
)
type GlobalConfig struct {
@@ -62,7 +68,7 @@ func WithOperationID(ctx context.Context, operationID string) context.Context {
return mcontext.SetOperationID(ctx, operationID)
}
func WithSendMessageCallback(ctx context.Context, callback open_im_sdk_callback.SendMsgCallBack) context.Context {
- return context.WithValue(ctx, Callback, callback)
+ return context.WithValue(ctx, CtxCallback, callback)
}
func WithApiErrCode(ctx context.Context, cb ApiErrCodeCallback) context.Context {
diff --git a/pkg/common/check.go b/pkg/common/check.go
deleted file mode 100644
index 67b2bded2..000000000
--- a/pkg/common/check.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package common
-
-import (
- "encoding/json"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "runtime"
-)
-
-//var validate *validator.Validate
-
-//funcation init() {
-// validate = validator.New()
-//}
-
-func CheckAnyErrCallback(callback open_im_sdk_callback.Base, errCode int32, err error, operationID string) {
- if err != nil {
- errInfo := "operationID[" + operationID + "], " + "info[" + err.Error() + "]"
- //log.NewError(operationID, "checkErr ", errInfo)
- callback.OnError(errCode, errInfo)
- runtime.Goexit()
- }
-}
-func CheckConfigErrCallback(callback open_im_sdk_callback.Base, err error, operationID string) {
- CheckAnyErrCallback(callback, sdkerrs.ArgsError, err, operationID)
-}
-
-//func CheckTokenErrCallback(callback open_im_sdk_callback.Base, err error, operationID string) {
-// CheckAnyErrCallback(callback, sdkerrs.TokenInvalidError, err, operationID)
-//}
-
-func CheckDBErrCallback(callback open_im_sdk_callback.Base, err error, operationID string) {
- CheckAnyErrCallback(callback, sdkerrs.SdkInternalError, err, operationID)
-}
-
-func CheckDataErrCallback(callback open_im_sdk_callback.Base, err error, operationID string) {
- CheckAnyErrCallback(callback, sdkerrs.SdkInternalError, err, operationID)
-}
-
-func CheckArgsErrCallback(callback open_im_sdk_callback.Base, err error, operationID string) {
- CheckAnyErrCallback(callback, sdkerrs.ArgsError, err, operationID)
-}
-
-//
-//funcation CheckErrAndResp2(err error, resp []byte, output interface{}) error {
-// if err != nil {
-// return utils.Wrap(err, "api resp failed")
-// }
-// var c server_api_params.CommDataResp
-// err = json.Unmarshal(resp, &c)
-// if err == nil {
-// if c.ErrCode != 0 {
-// return utils.Wrap(errors.New(c.ErrMsg), "")
-// }
-// if output != nil {
-// err = mapstructure.Decode(c.Data, output)
-// if err != nil {
-// goto one
-// }
-// return nil
-// }
-// return nil
-// }
-//
-// unMarshaler := jsonpb.Unmarshaler{}
-// unMarshaler.Unmarshal()
-// s, _ := marshaler.MarshalToString(pb)
-// out := make(map[string]interface{})
-// json.Unmarshal([]byte(s), &out)
-// if idFix {
-// if _, ok := out["id"]; ok {
-// out["_id"] = out["id"]
-// delete(out, "id")
-// }
-// }
-// return out
-//
-//one:
-// var c2 server_api_params.CommDataRespOne
-//
-// err = json.Unmarshal(resp, &c2)
-// if err != nil {
-// return utils.Wrap(err, "")
-// }
-// if c2.ErrCode != 0 {
-// return utils.Wrap(errors.New(c2.ErrMsg), "")
-// }
-// if output != nil {
-// err = mapstructure.Decode(c2.Data, output)
-// if err != nil {
-// return utils.Wrap(err, "")
-// }
-// return nil
-// }
-// return nil
-//}
-
-func JsonUnmarshalAndArgsValidate(s string, args interface{}, callback open_im_sdk_callback.Base, operationID string) error {
- err := json.Unmarshal([]byte(s), args)
- if err != nil {
- if callback != nil {
- //log.NewError(operationID, "Unmarshal failed ", err.Error(), s)
- callback.OnError(sdkerrs.ArgsError, err.Error())
- runtime.Goexit()
- } else {
- return utils.Wrap(err, "json Unmarshal failed")
- }
- }
- //err = validate.Struct(args)
- //if err != nil {
- // if callback != nil {
- // log.NewError(operationID, "validate failed ", err.Error(), s)
- // callback.OnError(constant.ErrArgs.ErrCode, constant.ErrArgs.ErrMsg)
- // runtime.Goexit()
- // }
- //}
- //return utils.Wrap(err, "args check failed")
- return nil
-}
-
-func JsonUnmarshalCallback(s string, args interface{}, callback open_im_sdk_callback.Base, operationID string) error {
- err := json.Unmarshal([]byte(s), args)
- if err != nil {
- if callback != nil {
- //log.NewError(operationID, "Unmarshal failed ", err.Error(), s)
- callback.OnError(sdkerrs.ArgsError, err.Error())
- runtime.Goexit()
- } else {
- return utils.Wrap(err, "json Unmarshal failed")
- }
- }
- return nil
-}
diff --git a/pkg/common/cmd.go b/pkg/common/cmd.go
new file mode 100644
index 000000000..697b5e409
--- /dev/null
+++ b/pkg/common/cmd.go
@@ -0,0 +1,90 @@
+package common
+
+import (
+ "context"
+ "fmt"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "time"
+
+ "github.com/openimsdk/tools/log"
+)
+
+var packet string
+
+func init() {
+ build, ok := debug.ReadBuildInfo()
+ if !ok {
+ return
+ }
+ packet = build.Main.Path
+ if packet != "" && !strings.HasSuffix(packet, "/") {
+ packet += "/"
+ }
+}
+
+type Cmd2Value struct {
+ Cmd string
+ Value any
+ Caller string
+ Ctx context.Context
+}
+
+func sendCmd(ch chan<- Cmd2Value, value Cmd2Value, timeout time.Duration) error {
+ if value.Caller == "" {
+ value.Caller = GetCaller(3)
+ }
+ if ch == nil {
+ log.ZError(value.Ctx, "sendCmd chan is nil", ErrChanNil, "caller", value.Caller, "cmd", value.Cmd, "value", value.Value)
+ return ErrChanNil
+ }
+ timer := time.NewTimer(timeout)
+ defer timer.Stop()
+ select {
+ case ch <- value:
+ log.ZInfo(value.Ctx, "sendCmd chan success", "caller", value.Caller, "cmd", value.Cmd, "value", value.Value)
+ return nil
+ case <-timer.C:
+ log.ZError(value.Ctx, "sendCmd chan timeout", ErrTimeout, "caller", value.Caller, "cmd", value.Cmd, "value", value.Value)
+ return ErrTimeout
+ }
+}
+
+func GetCaller(skip int) string {
+ pc, _, line, ok := runtime.Caller(skip)
+ if !ok {
+ return "runtime.caller.failed"
+ }
+ name := runtime.FuncForPC(pc).Name()
+ if packet != "" {
+ name = strings.TrimPrefix(name, packet)
+ }
+ return fmt.Sprintf("%s:%d", name, line)
+}
+
+type goroutine interface {
+ Work(cmd Cmd2Value)
+ GetCh() chan Cmd2Value
+}
+
+func DoListener(ctx context.Context, li goroutine) {
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Sprintf("panic: %+v\n%s", r, debug.Stack())
+ log.ZWarn(ctx, "DoListener panic", nil, "panic info", err)
+ }
+ }()
+
+ for {
+ select {
+ case cmd := <-li.GetCh():
+ log.ZInfo(cmd.Ctx, "recv cmd", "caller", cmd.Caller, "cmd", cmd.Cmd, "value", cmd.Value)
+ li.Work(cmd)
+ log.ZInfo(cmd.Ctx, "done cmd", "caller", cmd.Caller, "cmd", cmd.Cmd, "value", cmd.Value)
+ case <-ctx.Done():
+ log.ZInfo(ctx, "conversation done sdk logout.....")
+ return
+ }
+ }
+}
diff --git a/pkg/common/trigger_channel.go b/pkg/common/trigger_channel.go
index db41835cb..03f966e85 100644
--- a/pkg/common/trigger_channel.go
+++ b/pkg/common/trigger_channel.go
@@ -28,7 +28,12 @@ import (
"github.com/openimsdk/protocol/sdkws"
)
-var ErrChanNil = errs.New("channal == nil")
+const timeout = time.Millisecond * 10000
+
+var (
+ ErrChanNil = errs.New("channal == nil")
+ ErrTimeout = errors.New("send cmd timeout")
+)
func TriggerCmdNewMsgCome(ctx context.Context, msg sdk_struct.CmdNewMsgComeToConversation, conversationCh chan Cmd2Value) error {
if conversationCh == nil {
@@ -36,43 +41,56 @@ func TriggerCmdNewMsgCome(ctx context.Context, msg sdk_struct.CmdNewMsgComeToCon
}
c2v := Cmd2Value{Cmd: constant.CmdNewMsgCome, Value: msg, Ctx: ctx}
- return sendCmd(conversationCh, c2v, 100)
+ return sendCmd(conversationCh, c2v, timeout)
+}
+
+func TriggerCmdMsgSyncInReinstall(ctx context.Context, msg sdk_struct.CmdMsgSyncInReinstall, conversationCh chan Cmd2Value) error {
+ if conversationCh == nil {
+ return errs.Wrap(ErrChanNil)
+ }
+
+ c2v := Cmd2Value{Cmd: constant.CmdMsgSyncInReinstall, Value: msg, Ctx: ctx}
+ return sendCmd(conversationCh, c2v, timeout)
}
func TriggerCmdNotification(ctx context.Context, msg sdk_struct.CmdNewMsgComeToConversation, conversationCh chan Cmd2Value) {
c2v := Cmd2Value{Cmd: constant.CmdNotification, Value: msg, Ctx: ctx}
- err := sendCmd(conversationCh, c2v, 100)
+ err := sendCmd(conversationCh, c2v, timeout)
if err != nil {
log.ZWarn(ctx, "TriggerCmdNotification error", err, "msg", msg)
}
}
-func TriggerCmdWakeUp(ch chan Cmd2Value) error {
- if ch == nil {
- return errs.Wrap(ErrChanNil)
+func TriggerCmdSyncFlag(ctx context.Context, syncFlag int, conversationCh chan Cmd2Value) {
+ c2v := Cmd2Value{Cmd: constant.CmdSyncFlag, Value: sdk_struct.CmdNewMsgComeToConversation{SyncFlag: syncFlag}, Ctx: ctx}
+ err := sendCmd(conversationCh, c2v, timeout)
+ if err != nil {
+ log.ZWarn(ctx, "TriggerCmdNotification error", err, "syncFlag", syncFlag)
}
- c2v := Cmd2Value{Cmd: constant.CmdWakeUp, Value: nil}
- return sendCmd(ch, c2v, 100)
}
-func TriggerCmdSyncData(ch chan Cmd2Value) error {
+func TriggerCmdWakeUpDataSync(ctx context.Context, ch chan Cmd2Value) error {
if ch == nil {
return errs.Wrap(ErrChanNil)
}
- c2v := Cmd2Value{Cmd: constant.CmdSyncData, Value: nil}
- return sendCmd(ch, c2v, 100)
+ c2v := Cmd2Value{Cmd: constant.CmdWakeUpDataSync, Value: nil, Ctx: ctx}
+ return sendCmd(ch, c2v, timeout)
}
-func TriggerCmdSyncReactionExtensions(node SyncReactionExtensionsNode, conversationCh chan Cmd2Value) error {
- if conversationCh == nil {
+func TriggerCmdIMMessageSync(ctx context.Context, ch chan Cmd2Value) error {
+ if ch == nil {
return errs.Wrap(ErrChanNil)
}
- c2v := Cmd2Value{
- Cmd: constant.CmSyncReactionExtensions,
- Value: node,
- }
+ c2v := Cmd2Value{Cmd: constant.CmdIMMessageSync, Value: nil, Ctx: ctx}
+ return sendCmd(ch, c2v, timeout)
+}
- return sendCmd(conversationCh, c2v, 100)
+func TriggerCmdSyncData(ctx context.Context, ch chan Cmd2Value) {
+ c2v := Cmd2Value{Cmd: constant.CmdSyncData, Value: nil, Ctx: ctx}
+ err := sendCmd(ch, c2v, timeout)
+ if err != nil {
+ log.ZWarn(ctx, "TriggerCmdSyncData error", err)
+ }
}
func TriggerCmdUpdateConversation(ctx context.Context, node UpdateConNode, conversationCh chan<- Cmd2Value) error {
@@ -81,8 +99,11 @@ func TriggerCmdUpdateConversation(ctx context.Context, node UpdateConNode, conve
Value: node,
Ctx: ctx,
}
-
- return sendCmd(conversationCh, c2v, 100)
+ err := sendCmd(conversationCh, c2v, timeout)
+ if err != nil {
+ _, _ = len(conversationCh), cap(conversationCh)
+ }
+ return err
}
func TriggerCmdUpdateMessage(ctx context.Context, node UpdateMessageNode, conversationCh chan Cmd2Value) error {
@@ -91,27 +112,17 @@ func TriggerCmdUpdateMessage(ctx context.Context, node UpdateMessageNode, conver
Value: node,
Ctx: ctx,
}
-
- return sendCmd(conversationCh, c2v, 100)
+ return sendCmd(conversationCh, c2v, timeout)
}
-// Push message, msg for msgData slice
+// TriggerCmdPushMsg Push message, msg for msgData slice
func TriggerCmdPushMsg(ctx context.Context, msg *sdkws.PushMessages, ch chan Cmd2Value) error {
if ch == nil {
return errs.Wrap(ErrChanNil)
}
c2v := Cmd2Value{Cmd: constant.CmdPushMsg, Value: msg, Ctx: ctx}
- return sendCmd(ch, c2v, 100)
-}
-
-// seq trigger
-func TriggerCmdMaxSeq(ctx context.Context, seq *sdk_struct.CmdMaxSeqToMsgSync, ch chan Cmd2Value) error {
- if ch == nil {
- return errs.Wrap(ErrChanNil)
- }
- c2v := Cmd2Value{Cmd: constant.CmdMaxSeq, Value: seq, Ctx: ctx}
- return sendCmd(ch, c2v, 100)
+ return sendCmd(ch, c2v, timeout)
}
func TriggerCmdLogOut(ctx context.Context, ch chan Cmd2Value) error {
@@ -119,16 +130,16 @@ func TriggerCmdLogOut(ctx context.Context, ch chan Cmd2Value) error {
return errs.Wrap(ErrChanNil)
}
c2v := Cmd2Value{Cmd: constant.CmdLogOut, Ctx: ctx}
- return sendCmd(ch, c2v, 100)
+ return sendCmd(ch, c2v, timeout)
}
-// Connection success trigger
+// TriggerCmdConnected Connection success trigger
func TriggerCmdConnected(ctx context.Context, ch chan Cmd2Value) error {
if ch == nil {
return errs.Wrap(ErrChanNil)
}
c2v := Cmd2Value{Cmd: constant.CmdConnSuccesss, Value: nil, Ctx: ctx}
- return sendCmd(ch, c2v, 100)
+ return sendCmd(ch, c2v, timeout)
}
type DeleteConNode struct {
@@ -136,31 +147,30 @@ type DeleteConNode struct {
ConversationID string
SessionType int
}
+
type SyncReactionExtensionsNode struct {
OperationID string
Action int
Args interface{}
}
+
type UpdateConNode struct {
ConID string
Action int //1 Delete the conversation; 2 Update the latest news in the conversation or add a conversation; 3 Put a conversation on the top;
// 4 Cancel a conversation on the top, 5 Messages are not read and set to 0, 6 New conversations
Args interface{}
}
+
type UpdateMessageNode struct {
Action int
Args interface{}
}
-type Cmd2Value struct {
- Cmd string
- Value interface{}
- Ctx context.Context
-}
type UpdateConInfo struct {
UserID string
GroupID string
}
+
type UpdateMessageInfo struct {
SessionType int32
UserID string
@@ -175,36 +185,3 @@ type SourceIDAndSessionType struct {
FaceURL string
Nickname string
}
-
-func UnInitAll(conversationCh chan Cmd2Value) error {
- c2v := Cmd2Value{Cmd: constant.CmdUnInit}
- return sendCmd(conversationCh, c2v, 100)
-}
-
-type goroutine interface {
- Work(cmd Cmd2Value)
- GetCh() chan Cmd2Value
- //GetContext() context.Context
-}
-
-func DoListener(Li goroutine, ctx context.Context) {
- for {
- select {
- case cmd := <-Li.GetCh():
- Li.Work(cmd)
- case <-ctx.Done():
- log.ZInfo(ctx, "conversation done sdk logout.....")
- return
- }
- }
-
-}
-
-func sendCmd(ch chan<- Cmd2Value, value Cmd2Value, timeout int64) error {
- select {
- case ch <- value:
- return nil
- case <-time.After(time.Millisecond * time.Duration(timeout)):
- return errors.New("send cmd timeout")
- }
-}
diff --git a/pkg/common/wrap_error.go b/pkg/common/wrap_error.go
deleted file mode 100644
index 88a208547..000000000
--- a/pkg/common/wrap_error.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package common
-
-//import (
-// "github.com/mitchellh/mapstructure"
-// "open_im_sdk/open_im_sdk_callback"
-// "open_im_sdk/pkg/db"
-// "open_im_sdk/pkg/db/model_struct"
-//)
-//
-//funcation GetGroupMemberListByGroupID(callback open_im_sdk_callback.Base, operationID string, db *db.DataBase, groupID string) []*model_struct.LocalGroupMember {
-// memberList, err := db.GetGroupMemberListByGroupID(groupID)
-// CheckDBErrCallback(callback, err, operationID)
-// return memberList
-//}
-//
-//funcation MapstructureDecode(input interface{}, output interface{}, callback open_im_sdk_callback.Base, oprationID string) {
-// err := mapstructure.Decode(input, output)
-// CheckDataErrCallback(callback, err, oprationID)
-//}
diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go
index 080e3f365..861d151ae 100644
--- a/pkg/constant/constant.go
+++ b/pkg/constant/constant.go
@@ -15,13 +15,13 @@
package constant
const (
- CmdSyncData = "001"
- CmdBlackList = "002"
- CmdNotification = "003"
- CmdDeleteConversation = "004"
- CmdNewMsgCome = "005"
+ CmdSyncData = "syncData"
+ CmdSyncFlag = "syncFlag"
+ CmdNotification = "notification"
+ CmdMsgSyncInReinstall = "msgSyncInReinstall"
+ CmdNewMsgCome = "newMsgCome"
CmdSuperGroupMsgCome = "006"
- CmdUpdateConversation = "007"
+ CmdUpdateConversation = "updateConversation"
CmSyncReactionExtensions = "008"
CmdFroceSyncBlackList = "009"
CmdForceSyncFriendApplication = "010"
@@ -34,16 +34,17 @@ const (
CmdAddFriend = "017"
CmdJoinedSuperGroup = "018"
- CmdUpdateMessage = "019"
+ CmdUpdateMessage = "updateMessage"
CmdReconnect = "020"
CmdInit = "021"
- CmdMaxSeq = "maxSeq"
- CmdPushMsg = "pushMsg"
- CmdConnSuccesss = "connSuccess"
- CmdWakeUp = "wakeUp"
- CmdLogOut = "loginOut"
+ CmdMaxSeq = "maxSeq"
+ CmdPushMsg = "pushMsg"
+ CmdConnSuccesss = "connSuccess"
+ CmdWakeUpDataSync = "wakeUpDataSync"
+ CmdIMMessageSync = "imMessageSync"
+ CmdLogOut = "loginOut"
)
const (
@@ -64,11 +65,9 @@ const (
AdvancedText = 117
CustomMsgNotTriggerConversation = 119
CustomMsgOnlineOnly = 120
- ReactionMessageModifier = 121
- ReactionMessageDeleter = 122
- //////////////////////////////////////////
- NotificationBegin = 1000
+ NotificationBegin = 1000
+
FriendNotificationBegin = 1200
FriendApplicationApprovedNotification = 1201 //add_friend_response
@@ -92,7 +91,6 @@ const (
UserCommandUpdateNotification = 1307
UserNotificationEnd = 1399
- OANotification = 1400
GroupNotificationBegin = 1500
@@ -118,38 +116,18 @@ const (
GroupInfoSetNameNotification = 1520
GroupNotificationEnd = 1599
- SignalingNotificationBegin = 1600
- SignalingNotification = 1601
- SignalingNotificationEnd = 1649
-
- SuperGroupNotificationBegin = 1650
- SuperGroupUpdateNotification = 1651
- MsgDeleteNotification = 1652
- ReactionMessageModifierNotification = 1653
- ReactionMessageDeleteNotification = 1654
- SuperGroupNotificationEnd = 1699
-
ConversationPrivateChatNotification = 1701
- ConversationUnreadNotification = 1702
+ ClearConversationNotification = 1703
- WorkMomentNotificationBegin = 1900
- WorkMomentNotification = 1901
-
- BusinessNotificationBegin = 2000
- BusinessNotification = 2001
- BusinessNotificationEnd = 2099
+ BusinessNotification = 2001
RevokeNotification = 2101
- HasReadReceiptNotification = 2150
- GroupHasReadReceiptNotification = 2155
- ClearConversationNotification = 2101
- DeleteMsgsNotification = 2102
+ DeleteMsgsNotification = 2102
HasReadReceipt = 2200
NotificationEnd = 5000
-
////////////////////////////////////////
//MsgFrom
@@ -158,14 +136,12 @@ const (
/////////////////////////////////////
//SessionType
- SingleChatType = 1
- GroupChatType = 2
- SuperGroupChatType = 3
+ SingleChatType = 1
+ // WriteGroupChatType Not enabled temporarily
+ WriteGroupChatType = 2
+ ReadGroupChatType = 3
NotificationChatType = 4
- //MsgStatus
- MsgStatusDefault = 0
-
MsgStatusSending = 1
MsgStatusSendSuccess = 2
MsgStatusSendFailed = 3
@@ -187,153 +163,65 @@ const (
GroupBanChat = 1
GroupStatusDismissed = 2
GroupStatusMuted = 3
-
- // workMoment permission
- WorkMomentPublic = 0
- WorkMomentPrivate = 1
- WorkMomentPermissionCanSee = 2
- WorkMomentPermissionCantSee = 3
-
- // workMoment sdk notification type
- WorkMomentCommentNotification = 0
- WorkMomentLikeNotification = 1
- WorkMomentAtUserNotification = 2
)
-const (
- ckWsInitConnection string = "ws-init-connection"
- ckWsLoginConnection string = "ws-login-connection"
- ckWsClose string = "ws-close"
- ckWsKickOffLine string = "ws-kick-off-line"
- ckTokenExpired string = "token-expired"
- ckSelfInfoUpdate string = "self-info-update"
-)
const (
BlackRelationship = 0
FriendRelationship = 1
)
-// const (
-//
-// ErrCodeInitLogin = 1001
-// ErrCodeFriend = 2001
-// ErrCodeConversation = 3001
-// ErrCodeUserInfo = 4001
-// ErrCodeGroup = 5001
-//
-// )
const (
NormalGroup = 0
SuperGroup = 1
WorkingGroup = 2
- SuperGroupTableName = "local_super_groups"
SuperGroupErrChatLogsTableNamePre = "local_sg_err_chat_logs_"
ChatLogsTableNamePre = "chat_logs_"
)
const (
- SdkInit = 0
-
- LoginSuccess = 101
- Logining = 102
- LoginFailed = 103
-
- Logout = 201
-
- TokenFailedExpired = 701
- TokenFailedInvalid = 702
- TokenFailedKickedOffline = 703
-)
-
-const (
- DeFaultSuccessMsg = "ok"
-)
-
-const (
- AddConOrUpLatMsg = 2
- UnreadCountSetZero = 3
- IncrUnread = 5
- TotalUnreadMessageChanged = 6
- UpdateConFaceUrlAndNickName = 7
- UpdateLatestMessageChange = 8
- ConChange = 9
- NewCon = 10
- ConChangeDirect = 11
- NewConDirect = 12
- ConversationLatestMsgHasRead = 13
- UpdateMsgFaceUrlAndNickName = 14
- SyncConversation = 15
- SyncMessageListReactionExtensions = 16
- SyncMessageListTypeKeyInfo = 17
- UpdateUserCommand = 18
+ AddConOrUpLatMsg = 1
+ TotalUnreadMessageChanged = 2
+ UpdateConFaceUrlAndNickName = 3
+ UpdateLatestMessageReadState = 4
+ UpdateLatestMessageFaceUrlAndNickName = 5
+ ConChange = 6
+ NewCon = 7
+ ConChangeDirect = 8
+ NewConDirect = 9
+ UpdateMsgFaceUrlAndNickName = 10
HasRead = 1
NotRead = 0
-
- IsFilter = 1
- NotFilter = 0
)
const (
- GroupActionCreateGroup = 1
- GroupActionApplyJoinGroup = 2
- GroupActionQuitGroup = 3
- GroupActionSetGroupInfo = 4
- GroupActionKickGroupMember = 5
- GroupActionTransferGroupOwner = 6
- GroupActionInviteUserToGroup = 7
- GroupActionAcceptGroupApplication = 8
- GroupActionRefuseGroupApplication = 9
-)
-const ZoomScale = "200"
-const MaxTotalMsgLen = 51200
-
-// const MaxTotalMsgLen = 20480
-const (
- FriendAcceptTip = "You have successfully become friends, so start chatting"
- TransferGroupTip = "The owner of the group is transferred!"
- AcceptGroupTip = "%s join the group"
-)
-
-const (
- GetNewestSeq = 1001
- PullMsgBySeqList = 1002
- SendMsg = 1003
- SendSignalMsg = 1004
- DelMsg = 1005
- PushMsg = 2001
- KickOnlineMsg = 2002
- LogoutMsg = 2003
- SetBackgroundStatus = 2004
-
- WSDataError = 3001
+ GetNewestSeq = 1001
+ PullMsgByRange = 1002
+ SendMsg = 1003
+ SendSignalMsg = 1004
+ PullMsgBySeqList = 1005
+ GetConvMaxReadSeq = 1006
+ PullConvLastMessage = 1007
+ PushMsg = 2001
+ KickOnlineMsg = 2002
+ LogoutMsg = 2003
+ SetBackgroundStatus = 2004
+ WsSubUserOnlineStatus = 2005
)
// conversation
const (
//MsgReceiveOpt
- ReceiveMessage = 0
+ ReceiveMessage = 0
+ // NotReceiveMessage This option is currently disabled
NotReceiveMessage = 1
ReceiveNotNotifyMessage = 2
Online = 1
Offline = 0
-
- //pinned
- Pinned = 1
- NotPinned = 0
-
- //privateChat
- IsPrivateChat = true
- NotPrivateChat = false
)
-const SuccessCallbackDefault = "" // Default value for success callback
-
const (
- AppOrdinaryUsers = 1 // Application user type: ordinary user
- AppAdmin = 2 // Application user type: administrator
-
GroupOwner = 100 // Group member type: owner
GroupAdmin = 60 // Group member type: administrator
GroupOrdinaryUsers = 20 // Group member type: ordinary user
@@ -351,9 +239,6 @@ const (
FriendResponseAgree = 1 // Response to friend request: agree
FriendResponseRefuse = -1 // Response to friend request: refuse
FriendResponseDefault = 0
-
- Male = 1 // Gender: male
- Female = 2 // Gender: female
)
const (
AtAllString = "AtAllTag" // String for 'all people' mention tag
@@ -363,42 +248,15 @@ const (
AtAllAtMe = 3 // Mention mode: mention all people and sender
)
-const (
- FieldRecvMsgOpt = 1 // Field type: message receiving options
- FieldIsPinned = 2 // Field type: whether a message is pinned
- FieldAttachedInfo = 3 // Field type: attached information
- FieldIsPrivateChat = 4 // Field type: whether a message is from a private chat
- FieldGroupAtType = 5 // Field type: group mention mode
- FieldIsNotInGroup = 6 // Field type: whether a message is not in a group
- FieldEx = 7 // Field type: extension field
- FieldUnread = 8 // Field type: whether a message is unread
- FieldBurnDuration = 9 // Field type: message burn duration
-)
-const (
- SetMessageExtensions = 1 // Message extension operation type: set extension
- AddMessageExtensions = 2 // Message extension operation type: add extension
-)
+
const (
KeywordMatchOr = 0 // Keyword match mode: match any keyword
KeywordMatchAnd = 1 // Keyword match mode: match all keywords
)
const BigVersion = "v3"
-const UpdateVersion = ".0.0"
-const SdkVersion = "openim-sdk-core-"
-const LogFileName = "sdk"
-
-func GetSdkVersion() string {
- return SdkVersion + BigVersion + UpdateVersion
-}
-
-var HeartbeatInterval = 5
const (
- MsgSyncModelDefault = 0 //SyncFlag
- MsgSyncModelLogin = 1 //SyncFlag
- SyncOrderStartLatest = 101 //PullMsgOrder
-
MsgSyncBegin = 1001 //
MsgSyncProcessing = 1002 //
MsgSyncEnd = 1003 //
@@ -408,23 +266,16 @@ const (
)
const (
- JoinByInvitation = 2
- JoinBySearch = 3
- JoinByQRCode = 4
+ SplitPullMsgNum = 100
+ PullMsgNumForReadDiffusion = 50
)
+
const (
- SplitPullMsgNum = 100
- PullMsgNumWhenLogin = 10000
- PullMsgNumForReadDiffusion = 50
- NormalMsgMinNumReadDiffusion = 100
+ Uninitialized = -1001
)
-const SplitGetGroupMemberNum = 1000
-const UseHashGroupMemberNum = 1000
-
+// GroupApplicationReceiver
const (
- Uninitialized = -1001
- NoNetwork = 1 // Online -> Offline
- NetworkAvailable = 2 // Offline -> Online
- NetworkVariation = 3 // Online, but status changed
+ ApplicantReceiver = iota
+ AdminReceiver
)
diff --git a/pkg/constant/error.go b/pkg/constant/error.go
deleted file mode 100644
index 49f203f3d..000000000
--- a/pkg/constant/error.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package constant
-
-// key = errCode, string = errMsg
-//type ErrInfo struct {
-// ErrCode int32
-// ErrMsg string
-//}
-//
-//var (
-// OK = ErrInfo{0, ""}
-//
-// ErrParseToken = ErrInfo{200, ParseTokenMsg.Error()}
-//
-// ErrTencentCredential = ErrInfo{400, ThirdPartyMsg.Error()}
-// ErrInBlackList = ErrInfo{ErrCode: 600, ErrMsg: InBlackList.Error()}
-// ErrNotFriend = ErrInfo{ErrCode: 601, ErrMsg: NotFriend.Error()}
-//
-// ErrTokenExpired = ErrInfo{701, TokenExpiredMsg.Error()}
-// ErrTokenInvalid = ErrInfo{702, TokenInvalidMsg.Error()}
-// ErrTokenMalformed = ErrInfo{703, TokenMalformedMsg.Error()}
-// ErrTokenNotValidYet = ErrInfo{704, TokenNotValidYetMsg.Error()}
-// ErrTokenUnknown = ErrInfo{705, TokenUnknownMsg.Error()}
-// ErrTokenKicked = ErrInfo{706, TokenUserKickedMsg.Error()}
-//
-// ErrAccess = ErrInfo{ErrCode: 801, ErrMsg: AccessMsg.Error()}
-// ErrDB = ErrInfo{ErrCode: 802, ErrMsg: DBMsg.Error()}
-// ErrArgs = ErrInfo{ErrCode: 803, ErrMsg: ArgsMsg.Error()}
-// ErrApi = ErrInfo{ErrCode: 804, ErrMsg: ApiMsg.Error()}
-// ErrData = ErrInfo{ErrCode: 805, ErrMsg: DataMsg.Error()}
-// ErrLogin = ErrInfo{ErrCode: 806, ErrMsg: LoginMsg.Error()}
-// ErrConfig = ErrInfo{ErrCode: 807, ErrMsg: ConfigMsg.Error()}
-// ErrThirdParty = ErrInfo{ErrCode: 808, ErrMsg: ThirdPartyMsg.Error()}
-// ErrServerReturn = ErrInfo{ErrCode: 809, ErrMsg: ServerReturn.Error()}
-//
-// ErrWsRecvConnDiff = ErrInfo{ErrCode: 901, ErrMsg: WsRecvConnDiff.Error()}
-// ErrWsRecvConnSame = ErrInfo{ErrCode: 902, ErrMsg: WsRecvConnSame.Error()}
-// ErrWsRecvCode = ErrInfo{ErrCode: 903, ErrMsg: WsRecvCode.Error()}
-// ErrWsSendTimeout = ErrInfo{ErrCode: 904, ErrMsg: WsSendTimeout.Error()}
-// ErrResourceLoadNotComplete = ErrInfo{ErrCode: 905, ErrMsg: ResourceLoadNotComplete.Error()}
-// ErrNotSupportFunction = ErrInfo{ErrCode: 906, ErrMsg: NotSupportFunction.Error()}
-//)
-//
-//var (
-// ParseTokenMsg = errors.New("parse token failed")
-// TokenExpiredMsg = errors.New("token is timed out, please log in again")
-// TokenInvalidMsg = errors.New("token has been invalidated")
-// TokenNotValidYetMsg = errors.New("token not active yet")
-// TokenMalformedMsg = errors.New("that's not even a token")
-// TokenUnknownMsg = errors.New("couldn't handle this token")
-// TokenUserKickedMsg = errors.New("user has been kicked")
-//
-// AccessMsg = errors.New("no permission")
-// DBMsg = errors.New("db failed")
-// ArgsMsg = errors.New("args failed")
-// ApiMsg = errors.New("api failed")
-// DataMsg = errors.New("data failed ")
-// LoginMsg = errors.New("you can only login once")
-// ConfigMsg = errors.New("config failed")
-//
-// ThirdPartyMsg = errors.New("third party error")
-// ServerReturn = errors.New("server return data err")
-//
-// WsRecvConnDiff = errors.New("recv timeout, conn diff")
-// WsRecvConnSame = errors.New("recv timeout, conn same")
-// WsRecvCode = errors.New("recv code err")
-// WsSendTimeout = errors.New("send timeout")
-// ResourceLoadNotComplete = errors.New("resource loading is not complete")
-// NotSupportFunction = errors.New("unsupported function")
-//
-// NotFriend = errors.New("not friend")
-// InBlackList = errors.New("in blackList")
-//)
-//
-//funcation (e *ErrInfo) Error() string {
-// return e.ErrMsg
-//}
-//
-//const (
-// StatusErrTokenExpired = 701
-// StatusErrTokenInvalid = 702
-// StatusErrTokenMalformed = 703
-// StatusErrTokenNotValidYet = 704
-// StatusErrTokenUnknown = 705
-// StatusErrTokenKicked = 706
-//)
-//
-//var statusText = map[int]*ErrInfo{
-// StatusErrTokenExpired: &ErrTokenExpired,
-// StatusErrTokenInvalid: &ErrTokenInvalid,
-// StatusErrTokenMalformed: &ErrTokenMalformed,
-// StatusErrTokenNotValidYet: &ErrTokenNotValidYet,
-// StatusErrTokenUnknown: &ErrTokenUnknown,
-// StatusErrTokenKicked: &ErrTokenKicked,
-//}
-//
-//funcation StatusText(code int) *ErrInfo {
-// return statusText[code]
-//}
diff --git a/pkg/constant/group.go b/pkg/constant/group.go
deleted file mode 100644
index 497ac14ba..000000000
--- a/pkg/constant/group.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package constant
-
-// GroupApplicationReceiver
-const (
- ApplicantReceiver = iota
- AdminReceiver
-)
diff --git a/pkg/constant/platform_number_id_to_name.go b/pkg/constant/platform_number_id_to_name.go
deleted file mode 100644
index d84b45840..000000000
--- a/pkg/constant/platform_number_id_to_name.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package constant
-
-// fixme 1<--->IOS 2<--->Android 3<--->Windows
-//fixme 4<--->OSX 5<--->Web 6<--->MiniWeb 7<--->Linux
-
-const (
- //Platform ID
- IOSPlatformID = 1
- AndroidPlatformID = 2
- WindowsPlatformID = 3
- OSXPlatformID = 4
- WebPlatformID = 5
- MiniWebPlatformID = 6
- LinuxPlatformID = 7
- AndroidPadPlatformID = 8
- IPadPlatformID = 9
- AdminPlatformID = 10
-
- //Platform string match to Platform ID
- IOSPlatformStr = "IOS"
- AndroidPlatformStr = "Android"
- WindowsPlatformStr = "Windows"
- OSXPlatformStr = "OSX"
- WebPlatformStr = "Web"
- MiniWebPlatformStr = "MiniWeb"
- LinuxPlatformStr = "Linux"
- AndroidPadPlatformStr = "APad"
- IPadPlatformStr = "IPad"
- AdminPlatformStr = "Admin"
-
- //terminal types
- TerminalPC = "PC"
- TerminalMobile = "Mobile"
-)
-
-var PlatformID2Name = map[int]string{
- IOSPlatformID: IOSPlatformStr,
- AndroidPlatformID: AndroidPlatformStr,
- WindowsPlatformID: WindowsPlatformStr,
- OSXPlatformID: OSXPlatformStr,
- WebPlatformID: WebPlatformStr,
- MiniWebPlatformID: MiniWebPlatformStr,
- LinuxPlatformID: LinuxPlatformStr,
- AndroidPadPlatformID: AndroidPadPlatformStr,
- IPadPlatformID: IPadPlatformStr,
-}
-var PlatformName2ID = map[string]int{
- IOSPlatformStr: IOSPlatformID,
- AndroidPlatformStr: AndroidPlatformID,
- WindowsPlatformStr: WindowsPlatformID,
- OSXPlatformStr: OSXPlatformID,
- WebPlatformStr: WebPlatformID,
- MiniWebPlatformStr: MiniWebPlatformID,
- LinuxPlatformStr: LinuxPlatformID,
- AndroidPadPlatformStr: AndroidPadPlatformID,
- IPadPlatformStr: IPadPlatformID,
-}
-var Platform2class = map[string]string{
- IOSPlatformStr: TerminalMobile,
- AndroidPlatformStr: TerminalMobile,
- MiniWebPlatformStr: WebPlatformStr,
- WebPlatformStr: WebPlatformStr,
- WindowsPlatformStr: TerminalPC,
- OSXPlatformStr: TerminalPC,
- LinuxPlatformStr: TerminalPC,
-}
-
-func PlatformIDToName(num int) string {
- return PlatformID2Name[num]
-}
-func PlatformNameToID(name string) int {
- return PlatformName2ID[name]
-}
-func PlatformNameToClass(name string) string {
- return Platform2class[name]
-}
diff --git a/pkg/constant/server_api_router.go b/pkg/constant/server_api_router.go
deleted file mode 100644
index d2f9912c1..000000000
--- a/pkg/constant/server_api_router.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package constant
-
-const (
- GetSelfUserInfoRouter = "/user/get_self_user_info"
- GetUsersInfoRouter = "/user/get_users_info"
- UpdateSelfUserInfoRouter = "/user/update_user_info"
- UpdateSelfUserInfoExRouter = "/user/update_user_info_ex"
- SetGlobalRecvMessageOptRouter = "/user/set_global_msg_recv_opt"
- ProcessUserCommandAdd = "/user/process_user_command_add"
- ProcessUserCommandDelete = "/user/process_user_command_delete"
- ProcessUserCommandUpdate = "/user/process_user_command_update"
- ProcessUserCommandGet = "/user/process_user_command_get"
- ProcessUserCommandGetAll = "/user/process_user_command_get_all"
-
- GetUsersInfoFromCacheRouter = "/user/get_users_info_from_cache"
- AccountCheck = "/user/account_check"
- UserRegister = "/user/user_register"
- SubscribeUsersStatusRouter = "/user/subscribe_users_status"
- GetSubscribeUsersStatusRouter = "/user/get_subscribe_users_status"
- GetUserStatusRouter = "/user/get_users_status"
-
- AddFriendRouter = "/friend/add_friend"
- DeleteFriendRouter = "/friend/delete_friend"
- GetFriendApplicationListRouter = "/friend/get_friend_apply_list" //recv
- GetSelfFriendApplicationListRouter = "/friend/get_self_friend_apply_list" //send
- ImportFriendListRouter = "/friend/import_friend"
-
- GetDesignatedFriendsApplyRouter = "/friend/get_designated_friend_apply"
- GetFriendListRouter = "/friend/get_friend_list"
- GetDesignatedFriendsRouter = "/friend/get_designated_friends"
- AddFriendResponse = "/friend/add_friend_response"
- SetFriendRemark = "/friend/set_friend_remark"
- UpdateFriends = "/friend/update_friends"
- GetIncrementalFriends = "/friend/get_incremental_friends"
- GetFullFriendUserIDs = "/friend/get_full_friend_user_ids"
-
- AddBlackRouter = "/friend/add_black"
- RemoveBlackRouter = "/friend/remove_black"
- GetBlackListRouter = "/friend/get_black_list"
-
- PullUserMsgRouter = "/chat/pull_msg"
- PullUserMsgBySeqRouter = "/chat/pull_msg_by_seq"
- NewestSeqRouter = "/chat/newest_seq"
-
- // msg
- ClearConversationMsgRouter = RouterMsg + "/clear_conversation_msg" // Clear the message of the specified conversation
- ClearAllMsgRouter = RouterMsg + "/user_clear_all_msg" // Clear all messages of the current user
- DeleteMsgsRouter = RouterMsg + "/delete_msgs" // Delete the specified message
- RevokeMsgRouter = RouterMsg + "/revoke_msg"
- SetMessageReactionExtensionsRouter = RouterMsg + "/set_message_reaction_extensions"
- AddMessageReactionExtensionsRouter = RouterMsg + "/add_message_reaction_extensions"
- MarkMsgsAsReadRouter = RouterMsg + "/mark_msgs_as_read"
- GetConversationsHasReadAndMaxSeqRouter = RouterMsg + "/get_conversations_has_read_and_max_seq"
-
- MarkConversationAsRead = RouterMsg + "/mark_conversation_as_read"
- MarkMsgsAsRead = RouterMsg + "/mark_msgs_as_read"
- SetConversationHasReadSeq = RouterMsg + "/set_conversation_has_read_seq"
- SendMsgRouter = RouterMsg + "/send_msg"
- GetServerTimeRouter = RouterMsg + "/get_server_time"
-
- GetMessageListReactionExtensionsRouter = RouterMsg + "/get_message_list_reaction_extensions"
- DeleteMessageReactionExtensionsRouter = RouterMsg + "/delete_message_reaction_extensions"
-
- TencentCloudStorageCredentialRouter = "/third/tencent_cloud_storage_credential"
- AliOSSCredentialRouter = "/third/ali_oss_credential"
- MinioStorageCredentialRouter = "/third/minio_storage_credential"
- AwsStorageCredentialRouter = "/third/aws_storage_credential"
-
- // group
- CreateGroupRouter = RouterGroup + "/create_group"
- SetGroupInfoRouter = RouterGroup + "/set_group_info"
- JoinGroupRouter = RouterGroup + "/join_group"
- QuitGroupRouter = RouterGroup + "/quit_group"
- GetGroupsInfoRouter = RouterGroup + "/get_groups_info"
- GetGroupMemberListRouter = RouterGroup + "/get_group_member_list"
- GetGroupAllMemberListRouter = RouterGroup + "/get_group_all_member_list"
- GetGroupMembersInfoRouter = RouterGroup + "/get_group_members_info"
- InviteUserToGroupRouter = RouterGroup + "/invite_user_to_group"
- GetJoinedGroupListRouter = RouterGroup + "/get_joined_group_list"
- KickGroupMemberRouter = RouterGroup + "/kick_group"
- TransferGroupRouter = RouterGroup + "/transfer_group"
- GetRecvGroupApplicationListRouter = RouterGroup + "/get_recv_group_applicationList"
- GetSendGroupApplicationListRouter = RouterGroup + "/get_user_req_group_applicationList"
- AcceptGroupApplicationRouter = RouterGroup + "/group_application_response"
- RefuseGroupApplicationRouter = RouterGroup + "/group_application_response"
- DismissGroupRouter = RouterGroup + "/dismiss_group"
- MuteGroupMemberRouter = RouterGroup + "/mute_group_member"
- CancelMuteGroupMemberRouter = RouterGroup + "/cancel_mute_group_member"
- MuteGroupRouter = RouterGroup + "/mute_group"
- CancelMuteGroupRouter = RouterGroup + "/cancel_mute_group"
- SetGroupMemberNicknameRouter = RouterGroup + "/set_group_member_nickname"
- SetGroupMemberInfoRouter = RouterGroup + "/set_group_member_info"
- GetGroupAbstractInfoRouter = RouterGroup + "/get_group_abstract_info"
- //SearchGroupMember = RouterGroup + "/search_group_member"
- GetIncrementalJoinGroup = RouterGroup + "/get_incremental_join_groups"
- GetIncrementalGroupMemberBatch = RouterGroup + "/get_incremental_group_members_batch"
- GetFullJoinedGroupIDs = RouterGroup + "/get_full_join_group_ids"
- GetFullGroupMemberUserIDs = RouterGroup + "/get_full_group_member_user_ids"
-
- SetReceiveMessageOptRouter = "/conversation/set_receive_message_opt"
- GetReceiveMessageOptRouter = "/conversation/get_receive_message_opt"
- GetAllConversationMessageOptRouter = "/conversation/get_all_conversation_message_opt"
- SetConversationOptRouter = ConversationGroup + "/set_conversation"
- GetConversationsRouter = ConversationGroup + "/get_conversations"
- GetAllConversationsRouter = ConversationGroup + "/get_all_conversations"
- GetConversationRouter = ConversationGroup + "/get_conversation"
- BatchSetConversationRouter = ConversationGroup + "/batch_set_conversation"
- ModifyConversationFieldRouter = ConversationGroup + "/modify_conversation_field"
- SetConversationsRouter = ConversationGroup + "/set_conversations"
- GetIncrementalConversation = ConversationGroup + "/get_incremental_conversations"
- GetFullConversationIDs = ConversationGroup + "/get_full_conversation_ids"
- GetOwnerConversationRouter = ConversationGroup + "/get_owner_conversation"
-
- // organization
- GetSubDepartmentRouter = RouterOrganization + "/get_sub_department"
- GetDepartmentMemberRouter = RouterOrganization + "/get_department_member"
- ParseTokenRouter = RouterAuth + "/parse_token"
-
- // super_group
- GetJoinedSuperGroupListRouter = RouterSuperGroup + "/get_joined_group_list"
- GetSuperGroupsInfoRouter = RouterSuperGroup + "/get_groups_info"
-
- // third
- FcmUpdateTokenRouter = RouterThird + "/fcm_update_token"
- SetAppBadgeRouter = RouterThird + "/set_app_badge"
- UploadLogsRouter = RouterThird + "/logs/upload"
-
- // auth
- GetUsersToken = RouterAuth + "/user_token"
-)
-const (
- RouterGroup = "/group"
- ConversationGroup = "/conversation"
- RouterOrganization = "/organization"
- RouterAuth = "/auth"
- RouterSuperGroup = "/super_group"
- RouterMsg = "/msg"
- RouterThird = "/third"
-)
-
-const (
- ObjectPartLimit = "/object/part_limit"
- ObjectPartSize = "/object/part_size"
- ObjectInitiateMultipartUpload = "/object/initiate_multipart_upload"
- ObjectAuthSign = "/object/auth_sign"
- ObjectCompleteMultipartUpload = "/object/complete_multipart_upload"
- ObjectAccessURL = "/object/access_url"
-)
diff --git a/pkg/constant/server_error.go b/pkg/constant/server_error.go
deleted file mode 100644
index 88243f553..000000000
--- a/pkg/constant/server_error.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package constant
-
-//
-//var (
-// ErrServer = ErrInfo{500, "server error"}
-//
-// ErrTokenDifferentPlatformID = ErrInfo{707, TokenDifferentPlatformIDMsg.Error()}
-// ErrTokenDifferentUserID = ErrInfo{708, TokenDifferentUserIDMsg.Error()}
-//
-// ErrStatus = ErrInfo{ErrCode: 804, ErrMsg: StatusMsg.Error()}
-// ErrCallback = ErrInfo{ErrCode: 809, ErrMsg: CallBackMsg.Error()}
-// ErrSendLimit = ErrInfo{ErrCode: 810, ErrMsg: "send msg limit, to many request, try again later"}
-// ErrMessageHasReadDisable = ErrInfo{ErrCode: 811, ErrMsg: "message has read disable"}
-// ErrInternal = ErrInfo{ErrCode: 812, ErrMsg: "internal error"}
-//)
-//
-//var (
-// TokenDifferentPlatformIDMsg = errors.New("different platformID")
-// TokenDifferentUserIDMsg = errors.New("different userID")
-//
-// StatusMsg = errors.New("status is abnormal")
-//
-// CallBackMsg = errors.New("callback failed")
-//)
-//
-//const (
-// NoError = 0
-// FormattingError = 10001
-// HasRegistered = 10002
-// NotRegistered = 10003
-// PasswordErr = 10004
-// GetIMTokenErr = 10005
-// RepeatSendCode = 10006
-// MailSendCodeErr = 10007
-// SmsSendCodeErr = 10008
-// CodeInvalidOrExpired = 10009
-// RegisterFailed = 10010
-// ResetPasswordFailed = 10011
-// DatabaseError = 10002
-// ServerError = 10004
-// HttpError = 10005
-// IoError = 10006
-// IntentionalError = 10007
-//)
-//
-//funcation (e *ErrInfo) Code() int32 {
-// return e.ErrCode
-//}
diff --git a/pkg/content_type/content_type.go b/pkg/content_type/content_type.go
index 76c1788bc..2c741c80a 100644
--- a/pkg/content_type/content_type.go
+++ b/pkg/content_type/content_type.go
@@ -29,6 +29,7 @@ var ext = map[string]string{
"flv": "video/x-flv",
"webm": "video/webm",
"3gp": "video/3gpp",
+ "m4a": "audio/mp4",
"mp3": "audio/mpeg",
"wav": "audio/wav",
"ogg": "audio/ogg",
diff --git a/pkg/datafetcher/datafetcher.go b/pkg/datafetcher/datafetcher.go
index 14d96edce..58c21a591 100644
--- a/pkg/datafetcher/datafetcher.go
+++ b/pkg/datafetcher/datafetcher.go
@@ -2,6 +2,7 @@ package datafetcher
import (
"context"
+ "sort"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/tools/errs"
@@ -67,12 +68,16 @@ func (ds *DataFetcher[T]) FetchWithPagination(ctx context.Context, offset, limit
// FetchMissingAndFillLocal fetches missing data from server and fills local database
func (ds *DataFetcher[T]) FetchMissingAndFillLocal(ctx context.Context, uids []string) ([]T, error) {
+ if len(uids) == 0 {
+ return nil, nil
+ }
+
localData, needServer, err := ds.FetchFromLocal(ctx, uids)
if err != nil {
return nil, err
}
if !needServer {
- return localData, nil
+ return ds.sortByUserIDs(localData, uids), nil
}
localUIDSet := datautil.SliceSetAny(localData, ds.Key)
@@ -99,11 +104,30 @@ func (ds *DataFetcher[T]) FetchMissingAndFillLocal(ctx context.Context, uids []s
}
- return localData, nil
+ return ds.sortByUserIDs(localData, uids), nil
+}
+
+func (ds *DataFetcher[T]) sortByUserIDs(data []T, userIDs []string) []T {
+ userIndexMap := make(map[string]int, len(userIDs))
+ for i, uid := range userIDs {
+ userIndexMap[uid] = i
+ }
+ sort.SliceStable(data, func(i, j int) bool {
+ uid1 := ds.Key(data[i])
+ uid2 := ds.Key(data[j])
+ index1 := userIndexMap[uid1]
+ index2 := userIndexMap[uid2]
+ return index1 < index2
+ })
+
+ return data
}
// FetchMissingAndCombineLocal fetches missing data from the server and combines it with local data without inserting it into the local database
func (ds *DataFetcher[T]) FetchMissingAndCombineLocal(ctx context.Context, uids []string) ([]T, error) {
+ if len(uids) == 0 {
+ return nil, nil
+ }
localData, needServer, err := ds.FetchFromLocal(ctx, uids)
if err != nil {
return nil, err
diff --git a/pkg/db/admin_group_request_model.go b/pkg/db/admin_group_request_model.go
index 54d554efe..b58c1e749 100644
--- a/pkg/db/admin_group_request_model.go
+++ b/pkg/db/admin_group_request_model.go
@@ -26,20 +26,20 @@ import (
)
func (d *DataBase) InsertAdminGroupRequest(ctx context.Context, groupRequest *model_struct.LocalAdminGroupRequest) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupRequest).Error, "InsertAdminGroupRequest failed")
}
func (d *DataBase) DeleteAdminGroupRequest(ctx context.Context, groupID, userID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id=? and user_id=?", groupID, userID).Delete(&model_struct.LocalAdminGroupRequest{}).Error, "DeleteAdminGroupRequest failed")
}
func (d *DataBase) UpdateAdminGroupRequest(ctx context.Context, groupRequest *model_struct.LocalAdminGroupRequest) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(groupRequest).Select("*").Updates(*groupRequest)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -48,8 +48,8 @@ func (d *DataBase) UpdateAdminGroupRequest(ctx context.Context, groupRequest *mo
}
func (d *DataBase) GetAdminGroupApplication(ctx context.Context) ([]*model_struct.LocalAdminGroupRequest, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupRequestList []*model_struct.LocalAdminGroupRequest
err := errs.Wrap(d.conn.WithContext(ctx).Order("create_time DESC").Find(&groupRequestList).Error)
if err != nil {
diff --git a/testv2/app_version_test.go b/pkg/db/app_version_test.go
similarity index 79%
rename from testv2/app_version_test.go
rename to pkg/db/app_version_test.go
index e8692b03d..ce96106f7 100644
--- a/testv2/app_version_test.go
+++ b/pkg/db/app_version_test.go
@@ -1,17 +1,16 @@
-package testv2
+package db
import (
"context"
"testing"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/tools/log"
)
func Test_GetAppSDKVersion(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -26,7 +25,7 @@ func Test_GetAppSDKVersion(t *testing.T) {
func Test_SetAppSDKVersion(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
diff --git a/pkg/db/black_model.go b/pkg/db/black_model.go
index 41e3ad9eb..b4d090248 100644
--- a/pkg/db/black_model.go
+++ b/pkg/db/black_model.go
@@ -26,29 +26,29 @@ import (
)
func (d *DataBase) GetBlackListDB(ctx context.Context) ([]*model_struct.LocalBlack, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var blackList []*model_struct.LocalBlack
return blackList, errs.Wrap(d.conn.WithContext(ctx).Find(&blackList).Error)
}
func (d *DataBase) GetBlackListUserID(ctx context.Context) (blackListUid []string, err error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
return blackListUid, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalBlack{}).Select("block_user_id").Find(&blackListUid).Error, "GetBlackList failed")
}
func (d *DataBase) GetBlackInfoByBlockUserID(ctx context.Context, blockUserID string) (*model_struct.LocalBlack, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var black model_struct.LocalBlack
return &black, errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id = ? AND block_user_id = ? ",
d.loginUserID, blockUserID).Take(&black).Error, "GetBlackInfoByBlockUserID failed")
}
func (d *DataBase) GetBlackInfoList(ctx context.Context, blockUserIDList []string) ([]*model_struct.LocalBlack, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var blackList []*model_struct.LocalBlack
if err := d.conn.WithContext(ctx).Where("block_user_id IN ? ", blockUserIDList).Find(&blackList).Error; err != nil {
return nil, errs.WrapMsg(err, "GetBlackInfoList failed")
@@ -57,14 +57,14 @@ func (d *DataBase) GetBlackInfoList(ctx context.Context, blockUserIDList []strin
}
func (d *DataBase) InsertBlack(ctx context.Context, black *model_struct.LocalBlack) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(black).Error, "InsertBlack failed")
}
func (d *DataBase) UpdateBlack(ctx context.Context, black *model_struct.LocalBlack) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Updates(black)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -73,7 +73,7 @@ func (d *DataBase) UpdateBlack(ctx context.Context, black *model_struct.LocalBla
}
func (d *DataBase) DeleteBlack(ctx context.Context, blockUserID string) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id=? and block_user_id=?", d.loginUserID, blockUserID).Delete(&model_struct.LocalBlack{}).Error, "DeleteBlack failed")
}
diff --git a/pkg/db/chat_log_model.go b/pkg/db/chat_log_model.go
index 91fe72c49..98e3b2538 100644
--- a/pkg/db/chat_log_model.go
+++ b/pkg/db/chat_log_model.go
@@ -21,6 +21,9 @@ import (
"context"
"errors"
"fmt"
+ "strings"
+
+ "gorm.io/gorm"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
@@ -32,10 +35,12 @@ import (
)
func (d *DataBase) initChatLog(ctx context.Context, conversationID string) error {
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
tableName := utils.GetTableName(conversationID)
if !d.tableChecker.HasTable(tableName) {
createTableSQL := fmt.Sprintf(`
- CREATE TABLE %s (
+ CREATE TABLE "%s" (
client_msg_id CHAR(64),
server_msg_id CHAR(64),
send_id CHAR(64),
@@ -64,11 +69,11 @@ func (d *DataBase) initChatLog(ctx context.Context, conversationID string) error
if result := d.conn.Exec(createTableSQL); result.Error != nil {
return errs.WrapMsg(result.Error, "Create table failed", "table", tableName)
}
- result := d.conn.Exec(fmt.Sprintf("CREATE INDEX %s ON %s (seq)", "index_seq_"+conversationID, tableName))
+ result := d.conn.Exec(fmt.Sprintf("CREATE INDEX `%s` ON `%s` (seq)", "index_seq_"+conversationID, tableName))
if result.Error != nil {
return errs.WrapMsg(result.Error, "Create index_seq failed", "table", tableName, "index", "index_seq_"+conversationID)
}
- result = d.conn.Exec(fmt.Sprintf("CREATE INDEX %s ON %s (send_time)", "index_send_time_"+conversationID, tableName))
+ result = d.conn.Exec(fmt.Sprintf("CREATE INDEX `%s` ON `%s` (send_time)", "index_send_time_"+conversationID, tableName))
if result.Error != nil {
return errs.WrapMsg(result.Error, "Create index_send_time failed", "table", tableName, "index", "index_send_time_"+conversationID)
}
@@ -82,6 +87,8 @@ func (d *DataBase) checkTable(ctx context.Context, tableName string) bool {
}
func (d *DataBase) UpdateMessage(ctx context.Context, conversationID string, c *model_struct.LocalChatLog) error {
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Updates(c)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update ")
@@ -96,6 +103,12 @@ func (d *DataBase) UpdateMessageBySeq(ctx context.Context, conversationID string
}
func (d *DataBase) BatchInsertMessageList(ctx context.Context, conversationID string, MessageList []*model_struct.LocalChatLog) error {
+ err := d.initChatLog(ctx, conversationID)
+ if err != nil {
+ log.ZWarn(ctx, "initChatLog err", err)
+ return err
+ }
+
if MessageList == nil {
return nil
}
@@ -115,6 +128,8 @@ func (d *DataBase) GetMessage(ctx context.Context, conversationID string, client
log.ZWarn(ctx, "initChatLog err", err)
return nil, err
}
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var c model_struct.LocalChatLog
return &c, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where("client_msg_id = ?",
clientMsgID).Take(&c).Error, "GetMessage failed")
@@ -126,6 +141,8 @@ func (d *DataBase) GetMessageBySeq(ctx context.Context, conversationID string, s
log.ZWarn(ctx, "initChatLog err", err)
return nil, err
}
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var c model_struct.LocalChatLog
return &c, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where("seq = ?",
seq).Take(&c).Error, "GetMessage failed")
@@ -137,44 +154,37 @@ func (d *DataBase) UpdateMessageTimeAndStatus(ctx context.Context, conversationI
return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Model(model_struct.LocalChatLog{}).Where("client_msg_id=? And seq=?", clientMsgID, 0).
Updates(model_struct.LocalChatLog{Status: status, SendTime: sendTime, ServerMsgID: serverMsgID}).Error, "UpdateMessageStatusBySourceID failed")
}
-func (d *DataBase) GetMessageListNoTime(ctx context.Context, conversationID string,
- count int, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- err = d.initChatLog(ctx, conversationID)
- if err != nil {
+
+func (d *DataBase) GetMessageList(ctx context.Context, conversationID string, count int, startTime, startSeq int64, startClientMsgID string, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
+ if err = d.initChatLog(ctx, conversationID); err != nil {
log.ZWarn(ctx, "initChatLog err", err)
return nil, err
}
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var timeOrder string
- if isReverse {
- timeOrder = "send_time ASC"
- } else {
- timeOrder = "send_time DESC"
- }
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Order(timeOrder).Offset(0).Limit(count).Find(&result).Error, "GetMessageList failed")
- if err != nil {
- return nil, err
- }
- return result, err
-}
-func (d *DataBase) GetMessageList(ctx context.Context, conversationID string, count int, startTime int64, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var condition, timeOrder, timeSymbol string
if isReverse {
- timeOrder = "send_time ASC"
+ timeOrder = "send_time ASC,seq ASC"
timeSymbol = ">"
} else {
- timeOrder = "send_time DESC"
+ timeOrder = "send_time DESC,seq DESC"
timeSymbol = "<"
}
- condition = "send_time " + timeSymbol + " ?"
-
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition, startTime).
- Order(timeOrder).Offset(0).Limit(count).Find(&result).Error, "GetMessageList failed")
- if err != nil {
- return nil, err
+ if startTime > 0 {
+ condition = "send_time " + timeSymbol + " ? " +
+ "OR (send_time = ? AND (seq " + timeSymbol + " ? OR (seq = 0 AND client_msg_id != ?)))"
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).
+ Where(condition, startTime, startTime, startSeq, startClientMsgID).
+ Order(timeOrder).Offset(0).Limit(count).Find(&result).Error, "GetMessageList failed")
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Order(timeOrder).
+ Offset(0).Limit(count).Find(&result).Error, "GetMessageList failed")
+ if err != nil {
+ return nil, err
+ }
}
return result, err
}
@@ -203,100 +213,136 @@ func (d *DataBase) DeleteConversationMsgsBySeqs(ctx context.Context, conversatio
return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where("seq IN ?", seqs).Delete(model_struct.LocalChatLog{}).Error, "DeleteConversationMsgs failed")
}
-func (d *DataBase) SearchMessageByContentType(ctx context.Context, contentType []int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
- condition := fmt.Sprintf("send_time between %d and %d AND status <=%d And content_type IN ?", startTime, endTime, constant.MsgStatusSendFailed)
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition, contentType).Order("send_time DESC").Offset(offset).Limit(count).Find(&result).Error, "SearchMessage failed")
+func (d *DataBase) SearchMessageByContentType(ctx context.Context, contentType []int, senderUserIDList []string, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+
+ var condition strings.Builder
+ var args []interface{}
+
+ condition.WriteString("send_time between ? AND ? AND status <= ? AND content_type IN (?) ")
+ args = append(args, startTime, endTime, constant.MsgStatusSendFailed, contentType)
+
+ if len(senderUserIDList) != 0 {
+ condition.WriteString(" And send_id IN (?)")
+ args = append(args, senderUserIDList)
+ }
+
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition.String(), args...).Order("send_time DESC").Offset(offset).Limit(count).Find(&result).Error, "SearchMessage failed")
return result, err
}
-func (d *DataBase) SearchMessageByKeyword(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
- var condition string
- var subCondition string
+
+func (d *DataBase) SearchMessageByKeyword(ctx context.Context, contentType []int, senderUserIDList []string, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+
+ var condition strings.Builder
+ var subCondition strings.Builder
+ var args []interface{}
+
+ condition.WriteString(" send_time between ? AND ? AND status <= ? AND content_type IN (?)")
+ args = append(args, startTime, endTime, constant.MsgStatusSendFailed, contentType)
+
+ // Construct a sub-condition for SQL query based on keyword list and match type
if keywordListMatchType == constant.KeywordMatchOr {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
+ // Use OR logic if keywordListMatchType is KeywordMatchOr
+ subCondition.WriteString(" AND (")
+ for i, keyword := range keywordList {
+ if i > 0 {
+ subCondition.WriteString(" OR ")
}
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "or "
- }
+ subCondition.WriteString("content LIKE ?")
+ args = append(args, "%"+keyword+"%")
}
+ subCondition.WriteString(") ")
} else {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "and "
+ // Use AND logic for other keywordListMatchType
+ subCondition.WriteString(" AND (")
+ for i, keyword := range keywordList {
+ if i > 0 {
+ subCondition.WriteString(" AND ")
}
+
+ subCondition.WriteString("content LIKE ?")
+ args = append(args, "%"+keyword+"%")
}
+ subCondition.WriteString(") ")
}
- condition = fmt.Sprintf(" send_time between %d and %d AND status <=%d And content_type IN ? ", startTime, endTime, constant.MsgStatusSendFailed)
- condition += subCondition
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition, contentType).Order("send_time DESC").Offset(offset).Limit(count).Find(&result).Error, "InsertMessage failed")
+
+ condition.WriteString(subCondition.String())
+
+ if senderUserIDList != nil {
+ condition.WriteString(" And send_id IN (?)")
+ args = append(args, senderUserIDList)
+ }
+
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition.String(), args...).Order("send_time DESC").Offset(offset).Limit(count).Find(&result).Error, "SearchMessage failed")
+
return result, err
-} // SearchMessageByContentTypeAndKeyword searches for messages in the database that match specified content types and keywords within a given time range.
-func (d *DataBase) SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
- var condition string
- var subCondition string
+}
+
+// SearchMessageByContentTypeAndKeyword searches for messages in the database that match specified content types and keywords within a given time range.
+func (d *DataBase) SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, senderUserIDList []string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+
+ var condition strings.Builder
+ var subCondition strings.Builder
+ var args []interface{}
+
+ // Construct the main SQL condition string
+ condition.WriteString(" send_time between ? AND ? AND status <= ? AND content_type IN (?)")
+ args = append(args, startTime, endTime, constant.MsgStatusSendFailed, contentType)
// Construct a sub-condition for SQL query based on keyword list and match type
if keywordListMatchType == constant.KeywordMatchOr {
// Use OR logic if keywordListMatchType is KeywordMatchOr
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "or "
+ subCondition.WriteString(" AND (")
+ for i, keyword := range keywordList {
+ if i > 0 {
+ subCondition.WriteString(" OR ")
}
+
+ subCondition.WriteString("content LIKE ?")
+ args = append(args, "%"+keyword+"%")
}
+ subCondition.WriteString(") ")
} else {
// Use AND logic for other keywordListMatchType
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "and "
+ subCondition.WriteString(" AND (")
+ for i, keyword := range keywordList {
+ if i > 0 {
+ subCondition.WriteString(" AND ")
}
+
+ subCondition.WriteString("content LIKE ?")
+ args = append(args, "%"+keyword+"%")
}
+ subCondition.WriteString(") ")
}
- // Construct the main SQL condition string
- condition = fmt.Sprintf("send_time between %d and %d AND status <=%d And content_type IN ? ", startTime, endTime, constant.MsgStatusSendFailed)
- condition += subCondition
+ condition.WriteString(subCondition.String())
+
+ if senderUserIDList != nil {
+ condition.WriteString(" And send_id IN (?)")
+ args = append(args, senderUserIDList)
+ }
// Execute the query using the constructed condition and handle errors
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition, contentType).Order("send_time DESC").Find(&result).Error, "SearchMessage failed")
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where(condition.String(), args...).Order("send_time DESC").Find(&result).Error, "SearchMessage failed")
return result, err
}
func (d *DataBase) UpdateMsgSenderFaceURLAndSenderNickname(ctx context.Context, conversationID, sendID, faceURL, nickname string) error {
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Model(model_struct.LocalChatLog{}).Where(
"send_id = ?", sendID).Updates(
map[string]interface{}{"sender_face_url": faceURL, "sender_nick_name": nickname}).Error, utils.GetSelfFuncName()+" failed")
}
-func (d *DataBase) GetAlreadyExistSeqList(ctx context.Context, conversationID string, lostSeqList []int64) (seqList []int64, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Where("seq IN ?", lostSeqList).Pluck("seq", &seqList).Error, utils.GetSelfFuncName()+" failed")
- if err != nil {
- return nil, err
- }
- return seqList, nil
-}
-
func (d *DataBase) UpdateColumnsMessage(ctx context.Context, conversationID, ClientMsgID string, args map[string]interface{}) error {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -308,12 +354,14 @@ func (d *DataBase) UpdateColumnsMessage(ctx context.Context, conversationID, Cli
return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed")
}
func (d *DataBase) SearchAllMessageByContentType(ctx context.Context, conversationID string, contentType int) (result []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
err = d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Model(&model_struct.LocalChatLog{}).Where("content_type = ?", contentType).Find(&result).Error
return result, err
}
func (d *DataBase) GetUnreadMessage(ctx context.Context, conversationID string) (msgs []*model_struct.LocalChatLog, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Debug().Where("send_id != ? AND is_read = ?", d.loginUserID, constant.NotRead).Find(&msgs).Error, "GetMessageList failed")
return msgs, err
}
@@ -347,10 +395,6 @@ func (d *DataBase) MarkConversationMessageAsReadDB(ctx context.Context, conversa
rowsAffected++
}
}
- // t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Where("client_msg_id in ? AND send_id != ?", msgIDs, d.loginUserID).Update("is_read", constant.HasRead)
- // if t.RowsAffected == 0 {
- // return 0, errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- // }
return rowsAffected, nil
}
@@ -365,11 +409,15 @@ func (d *DataBase) MarkConversationAllMessageAsRead(ctx context.Context, convers
}
func (d *DataBase) GetMessagesByClientMsgIDs(ctx context.Context, conversationID string, msgIDs []string) (msgs []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Where("client_msg_id IN ?", msgIDs).Order("send_time DESC").Find(&msgs).Error, "GetMessagesByClientMsgIDs error")
return msgs, err
}
func (d *DataBase) GetMessagesBySeqs(ctx context.Context, conversationID string, seqs []int64) (msgs []*model_struct.LocalChatLog, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Where("seq IN ?", seqs).Order("send_time DESC").Find(&msgs).Error, "GetMessagesBySeqs error")
return msgs, err
}
@@ -380,29 +428,31 @@ func (d *DataBase) GetConversationNormalMsgSeq(ctx context.Context, conversation
log.ZWarn(ctx, "initChatLog err", err)
return 0, err
}
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var seq int64
err = d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Select("IFNULL(max(seq),0)").Find(&seq).Error
return seq, errs.WrapMsg(err, "GetConversationNormalMsgSeq")
}
-func (d *DataBase) GetConversationNormalMsgSeqNoInit(ctx context.Context, conversationID string) (int64, error) {
+func (d *DataBase) CheckConversationNormalMsgSeq(ctx context.Context, conversationID string) (int64, error) {
var seq int64
- err := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Select("IFNULL(max(seq),0)").Find(&seq).Error
- return seq, errs.WrapMsg(err, "GetConversationNormalMsgSeq")
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+ if d.tableChecker.HasTable(utils.GetConversationTableName(conversationID)) {
+ err := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Select("IFNULL(max(seq),0)").Find(&seq).Error
+ return seq, errs.Wrap(err)
+ }
+ return 0, nil
}
func (d *DataBase) GetConversationPeerNormalMsgSeq(ctx context.Context, conversationID string) (int64, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var seq int64
err := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(conversationID)).Select("IFNULL(max(seq),0)").Where("send_id != ?", d.loginUserID).Find(&seq).Error
return seq, errs.WrapMsg(err, "GetConversationPeerNormalMsgSeq")
}
-func (d *DataBase) UpdateMsgSenderNickname(ctx context.Context, sendID, nickname string, sType int) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Where(
- "send_id = ? and session_type = ? and sender_nick_name != ? ", sendID, sType, nickname).Updates(
- map[string]interface{}{"sender_nick_name": nickname}).Error, utils.GetSelfFuncName()+" failed")
-}
func (d *DataBase) UpdateMsgSenderFaceURL(ctx context.Context, sendID, faceURL string, sType int) error {
d.mRWMutex.Lock()
@@ -411,3 +461,60 @@ func (d *DataBase) UpdateMsgSenderFaceURL(ctx context.Context, sendID, faceURL s
"send_id = ? and session_type = ? and sender_face_url != ? ", sendID, sType, faceURL).Updates(
map[string]interface{}{"sender_face_url": faceURL}).Error, utils.GetSelfFuncName()+" failed")
}
+
+func (d *DataBase) GetLatestActiveMessage(ctx context.Context, conversationID string, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
+ if err = d.initChatLog(ctx, conversationID); err != nil {
+ log.ZWarn(ctx, "initChatLog err", err)
+ return nil, err
+ }
+
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+
+ var timeOrder string
+ if isReverse {
+ timeOrder = "send_time ASC"
+ } else {
+ timeOrder = "send_time DESC"
+ }
+
+ // only get status < 4(NotHasDeleted) Msg
+ err = errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).Where("status < ?", constant.MsgStatusHasDeleted).Order(timeOrder).Offset(0).Limit(1).Find(&result).Error, "GetLatestActiveMessage failed")
+ if err != nil {
+ return nil, err
+ }
+
+ return result, err
+}
+func (d *DataBase) GetLatestValidServerMessage(ctx context.Context, conversationID string, startTime int64, isReverse bool) (*model_struct.LocalChatLog, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+
+ var condition, timeOrder, timeSymbol string
+ var result model_struct.LocalChatLog
+
+ if isReverse {
+ timeOrder = "send_time DESC"
+ timeSymbol = "<"
+ } else {
+ timeOrder = "send_time ASC"
+ timeSymbol = ">"
+ }
+
+ condition = "send_time " + timeSymbol + " ? AND seq != ?"
+
+ err := d.conn.WithContext(ctx).Table(utils.GetTableName(conversationID)).
+ Where(condition, startTime, 0).
+ Order(timeOrder).
+ Limit(1).
+ First(&result).Error
+
+ if err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, nil
+ }
+ return nil, errs.WrapMsg(err, "GetLatestValidServerMessage failed")
+ }
+
+ return &result, nil
+}
diff --git a/pkg/db/chat_log_model_test.go b/pkg/db/chat_log_model_test.go
new file mode 100644
index 000000000..5cd5e1085
--- /dev/null
+++ b/pkg/db/chat_log_model_test.go
@@ -0,0 +1,21 @@
+package db
+
+import (
+ "context"
+ "testing"
+)
+
+func TestGetLatestValidateServerMessage(t *testing.T) {
+ ctx := context.Background()
+ db, err := NewDataBase(ctx, "1695766238", "../../", 6)
+ if err != nil {
+ return
+ }
+
+ message, err := db.GetLatestValidServerMessage(ctx, "sg_93606743", 1710774468519, true)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log("message", message)
+}
diff --git a/pkg/db/chat_log_reaction_extension_model.go b/pkg/db/chat_log_reaction_extension_model.go
deleted file mode 100644
index cb679563a..000000000
--- a/pkg/db/chat_log_reaction_extension_model.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
- "errors"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/tools/errs"
-)
-
-func (d *DataBase) GetMessageReactionExtension(ctx context.Context, msgID string) (result *model_struct.LocalChatLogReactionExtensions, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var l model_struct.LocalChatLogReactionExtensions
- return &l, errs.WrapMsg(d.conn.WithContext(ctx).Where("client_msg_id = ?",
- msgID).Take(&l).Error, "GetMessageReactionExtension failed")
-}
-
-func (d *DataBase) InsertMessageReactionExtension(ctx context.Context, messageReactionExtension *model_struct.LocalChatLogReactionExtensions) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Create(messageReactionExtension).Error, "InsertMessageReactionExtension failed")
-}
-func (d *DataBase) UpdateMessageReactionExtension(ctx context.Context, c *model_struct.LocalChatLogReactionExtensions) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Updates(c)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateConversation failed")
-}
-
-// func (d *DataBase) GetAndUpdateMessageReactionExtension(ctx context.Context, msgID string, m map[string]*sdkws.KeyValue) error {
-// d.mRWMutex.Lock()
-// defer d.mRWMutex.Unlock()
-// var temp model_struct.LocalChatLogReactionExtensions
-// err := d.conn.WithContext(ctx).Where("client_msg_id = ?",
-// msgID).Take(&temp).Error
-// if err != nil {
-// temp.ClientMsgID = msgID
-// temp.LocalReactionExtensions = []byte(utils.StructToJsonString(m))
-// return d.conn.WithContext(ctx).Create(&temp).Error
-// } else {
-// oldKeyValue := make(map[string]*sdkws.KeyValue)
-// err = json.Unmarshal(temp.LocalReactionExtensions, &oldKeyValue)
-// if err != nil {
-// log.Error("special handle", err.Error())
-// }
-// log.Warn("special handle", oldKeyValue)
-// for k, newValue := range m {
-// oldKeyValue[k] = newValue
-// }
-// temp.LocalReactionExtensions = []byte(utils.StructToJsonString(oldKeyValue))
-// t := d.conn.WithContext(ctx).Updates(temp)
-// if t.RowsAffected == 0 {
-// return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
-// }
-// }
-// return nil
-// }
-func (d *DataBase) DeleteMessageReactionExtension(ctx context.Context, msgID string) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- temp := model_struct.LocalChatLogReactionExtensions{ClientMsgID: msgID}
- return d.conn.WithContext(ctx).Delete(&temp).Error
-
-}
-
-// func (d *DataBase) DeleteAndUpdateMessageReactionExtension(ctx context.Context, msgID string, m map[string]*sdkws.KeyValue) error {
-// d.mRWMutex.Lock()
-// defer d.mRWMutex.Unlock()
-// var temp model_struct.LocalChatLogReactionExtensions
-// err := d.conn.WithContext(ctx).Where("client_msg_id = ?",
-// msgID).Take(&temp).Error
-// if err != nil {
-// return err
-// } else {
-// oldKeyValue := make(map[string]*server_api_params.KeyValue)
-// _ = json.Unmarshal(temp.LocalReactionExtensions, &oldKeyValue)
-// for k := range m {
-// if _, ok := oldKeyValue[k]; ok {
-// delete(oldKeyValue, k)
-// }
-// }
-// temp.LocalReactionExtensions = []byte(utils.StructToJsonString(oldKeyValue))
-// t := d.conn.WithContext(ctx).Updates(temp)
-// if t.RowsAffected == 0 {
-// return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
-// }
-// }
-// return nil
-// }
-// func (d *DataBase) GetMultipleMessageReactionExtension(ctx context.Context, msgIDList []string) (result []*model_struct.LocalChatLogReactionExtensions, err error) {
-// d.mRWMutex.Lock()
-// defer d.mRWMutex.Unlock()
-// var messageList []model_struct.LocalChatLogReactionExtensions
-// err = errs.WrapMsg(d.conn.WithContext(ctx).Where("client_msg_id IN ?", msgIDList).Find(&messageList).Error, "GetMultipleMessageReactionExtension failed")
-// for _, v := range messageList {
-// v1 := v
-// result = append(result, &v1)
-// }
-// return result, err
-// }
diff --git a/pkg/db/conversation_model.go b/pkg/db/conversation_model.go
index 8f47ba3c6..6aa7927e2 100644
--- a/pkg/db/conversation_model.go
+++ b/pkg/db/conversation_model.go
@@ -37,14 +37,16 @@ const (
)
func (d *DataBase) GetConversationByUserID(ctx context.Context, userID string) (*model_struct.LocalConversation, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversation model_struct.LocalConversation
err := errs.WrapMsg(d.conn.WithContext(ctx).Where("user_id=?", userID).Find(&conversation).Error, "GetConversationByUserID error")
return &conversation, err
}
func (d *DataBase) GetAllConversationListDB(ctx context.Context) ([]*model_struct.LocalConversation, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversationList []*model_struct.LocalConversation
err := errs.WrapMsg(d.conn.WithContext(ctx).Where("latest_msg_send_time > ?", 0).Order("case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) DESC").Find(&conversationList).Error,
"GetAllConversationList failed")
@@ -53,43 +55,55 @@ func (d *DataBase) GetAllConversationListDB(ctx context.Context) ([]*model_struc
}
return conversationList, err
}
+
func (d *DataBase) FindAllConversationConversationID(ctx context.Context) (conversationIDs []string, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
return conversationIDs, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("latest_msg_send_time > ?", 0).Pluck("conversation_id", &conversationIDs).Error, "")
}
+
+func (d *DataBase) FindAllUnreadConversationConversationID(ctx context.Context) (conversationIDs []string, err error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
+ return conversationIDs, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("unread_count > ?", 0).Pluck("conversation_id", &conversationIDs).Error, "")
+}
+
func (d *DataBase) GetHiddenConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversationList []*model_struct.LocalConversation
return conversationList, errs.WrapMsg(d.conn.WithContext(ctx).Where("latest_msg_send_time = ?", 0).Find(&conversationList).Error,
"GetHiddenConversationList failed")
}
func (d *DataBase) GetAllConversations(ctx context.Context) ([]*model_struct.LocalConversation, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversationList []*model_struct.LocalConversation
return conversationList, errs.WrapMsg(d.conn.WithContext(ctx).Find(&conversationList).Error, "GetAllConversations failed")
}
func (d *DataBase) GetAllConversationIDList(ctx context.Context) (result []string, err error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var c model_struct.LocalConversation
return result, errs.WrapMsg(d.conn.WithContext(ctx).Model(&c).Pluck("conversation_id", &result).Error, "GetAllConversationIDList failed ")
}
func (d *DataBase) GetAllSingleConversationIDList(ctx context.Context) (result []string, err error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var c model_struct.LocalConversation
return result, errs.WrapMsg(d.conn.WithContext(ctx).Model(&c).Where("conversation_type = ?", constant.SingleChatType).Pluck("conversation_id", &result).Error, "GetAllConversationIDList failed ")
}
func (d *DataBase) GetConversationListSplitDB(ctx context.Context, offset, count int) ([]*model_struct.LocalConversation, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversationList []*model_struct.LocalConversation
- return conversationList, errs.WrapMsg(d.conn.WithContext(ctx).Where("latest_msg_send_time > ?", 0).Order("case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) DESC").Offset(offset).Limit(count).Find(&conversationList).Error,
- "GetFriendList failed")
+ return conversationList, errs.Wrap(d.conn.WithContext(ctx).Where("latest_msg_send_time > ?", 0).Order("case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) DESC").Offset(offset).Limit(count).Find(&conversationList).Error)
}
+
func (d *DataBase) BatchInsertConversationList(ctx context.Context, conversationList []*model_struct.LocalConversation) error {
if conversationList == nil {
return nil
@@ -114,6 +128,8 @@ func (d *DataBase) BatchInsertConversationList(ctx context.Context, conversation
}
func (d *DataBase) UpdateOrCreateConversations(ctx context.Context, conversationList []*model_struct.LocalConversation) error {
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
var conversationIDs []string
if err := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Pluck("conversation_id", &conversationIDs).Error; err != nil {
return err
@@ -160,6 +176,8 @@ func (d *DataBase) DeleteAllConversation(ctx context.Context) error {
}
func (d *DataBase) GetConversation(ctx context.Context, conversationID string) (*model_struct.LocalConversation, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var c model_struct.LocalConversation
return &c, errs.WrapMsg(d.conn.WithContext(ctx).Where("conversation_id = ?",
conversationID).Take(&c).Error, "GetConversation failed, conversationID: "+conversationID)
@@ -199,9 +217,10 @@ func (d *DataBase) BatchUpdateConversationList(ctx context.Context, conversation
}
return nil
}
+
func (d *DataBase) ConversationIfExists(ctx context.Context, conversationID string) (bool, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var count int64
t := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("conversation_id = ?",
conversationID).Count(&count)
@@ -266,6 +285,7 @@ func (d *DataBase) SetConversationDraftDB(ctx context.Context, conversationID, d
}
return errs.WrapMsg(t.Error, "SetConversationDraft failed")
}
+
func (d *DataBase) RemoveConversationDraft(ctx context.Context, conversationID, draftText string) error {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -276,6 +296,7 @@ func (d *DataBase) RemoveConversationDraft(ctx context.Context, conversationID,
}
return errs.WrapMsg(t.Error, "RemoveConversationDraft failed")
}
+
func (d *DataBase) UnPinConversation(ctx context.Context, conversationID string, isPinned int) error {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -296,6 +317,7 @@ func (d *DataBase) UpdateColumnsConversation(ctx context.Context, conversationID
}
return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed")
}
+
func (d *DataBase) UpdateAllConversation(ctx context.Context, conversation *model_struct.LocalConversation) error {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -308,6 +330,7 @@ func (d *DataBase) UpdateAllConversation(ctx context.Context, conversation *mode
}
return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed")
}
+
func (d *DataBase) IncrConversationUnreadCount(ctx context.Context, conversationID string) error {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -318,9 +341,10 @@ func (d *DataBase) IncrConversationUnreadCount(ctx context.Context, conversation
}
return errs.WrapMsg(t.Error, "IncrConversationUnreadCount failed")
}
+
func (d *DataBase) GetTotalUnreadMsgCountDB(ctx context.Context) (totalUnreadCount int32, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var result []int64
err = d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("recv_msg_opt < ? and latest_msg_send_time > ?", constant.ReceiveNotNotifyMessage, 0).Pluck("unread_count", &result).Error
if err != nil {
@@ -343,8 +367,8 @@ func (d *DataBase) SetMultipleConversationRecvMsgOpt(ctx context.Context, conver
}
func (d *DataBase) GetMultipleConversationDB(ctx context.Context, conversationIDList []string) (result []*model_struct.LocalConversation, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var conversationList []model_struct.LocalConversation
err = errs.WrapMsg(d.conn.WithContext(ctx).Where("conversation_id IN ?", conversationIDList).Find(&conversationList).Error, "GetMultipleConversation failed")
for _, v := range conversationList {
@@ -353,6 +377,7 @@ func (d *DataBase) GetMultipleConversationDB(ctx context.Context, conversationID
}
return result, err
}
+
func (d *DataBase) DecrConversationUnreadCount(ctx context.Context, conversationID string, count int64) (err error) {
d.mRWMutex.Lock()
defer d.mRWMutex.Unlock()
@@ -377,7 +402,10 @@ func (d *DataBase) DecrConversationUnreadCount(ctx context.Context, conversation
tx.Commit()
return nil
}
+
func (d *DataBase) SearchConversations(ctx context.Context, searchParam string) ([]*model_struct.LocalConversation, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
// Define the search condition based on the searchParam
condition := fmt.Sprintf("show_name like %q ", "%"+searchParam+"%")
var conversationList []*model_struct.LocalConversation
diff --git a/pkg/db/db_init.go b/pkg/db/db_init.go
index ac7f984c1..faa34919f 100644
--- a/pkg/db/db_init.go
+++ b/pkg/db/db_init.go
@@ -69,33 +69,11 @@ func (tc *TableChecker) UpdateTable(tableName string) {
}
type DataBase struct {
- loginUserID string
- dbDir string
- conn *gorm.DB
- tableChecker *TableChecker
- mRWMutex sync.RWMutex
- groupMtx sync.RWMutex
- friendMtx sync.RWMutex
- userMtx sync.RWMutex
- versionMtx sync.RWMutex
- superGroupMtx sync.RWMutex
-}
-
-func (d *DataBase) GetMultipleMessageReactionExtension(ctx context.Context, msgIDList []string) (result []*model_struct.LocalChatLogReactionExtensions, err error) {
- // TODO implement me
- panic("implement me")
-}
-
-func (d *DataBase) InitSuperLocalErrChatLog(ctx context.Context, groupID string) {
- panic("implement me")
-}
-
-func (d *DataBase) InitSuperLocalChatLog(ctx context.Context, groupID string) {
- panic("implement me")
-}
-
-func (d *DataBase) SetChatLogFailedStatus(ctx context.Context) {
- panic("implement me")
+ loginUserID string
+ dbDir string
+ conn *gorm.DB
+ tableChecker *TableChecker
+ mRWMutex sync.RWMutex
}
func (d *DataBase) InitDB(ctx context.Context, userID string, dataDir string) error {
@@ -147,17 +125,21 @@ func (d *DataBase) initDB(ctx context.Context, logLevel int) error {
}
log.ZInfo(ctx, "sqlite", "path", dbFileName)
// slowThreshold := 500
- // sqlLogger := log.NewSqlLogger(logger.LogLevel(sdk_struct.SvrConf.LogLevel), true, time.Duration(slowThreshold)*time.Millisecond)
+ // sqlLogger := log.NewSqlLogger(logger.LogLevel(sdk_struct.ServerConf.LogLevel), true, time.Duration(slowThreshold)*time.Millisecond)
if logLevel > 5 {
zLogLevel = logger.Info
} else {
zLogLevel = logger.Silent
}
- db, err := gorm.Open(sqlite.Open(dbFileName), &gorm.Config{Logger: log.NewSqlLogger(zLogLevel, false, time.Millisecond*200)})
+ var (
+ db *gorm.DB
+ )
+ db, err = gorm.Open(sqlite.Open(dbFileName), &gorm.Config{Logger: log.NewSqlLogger(zLogLevel, false, time.Millisecond*200)})
if err != nil {
return errs.WrapMsg(err, "open db failed "+dbFileName)
}
- log.ZDebug(ctx, "open db success", "db", db, "dbFileName", dbFileName)
+
+ log.ZDebug(ctx, "open db success", "dbFileName", dbFileName)
sqlDB, err := db.DB()
if err != nil {
return errs.WrapMsg(err, "get sql db failed")
@@ -195,16 +177,12 @@ func (d *DataBase) versionDataMigrate(ctx context.Context) error {
&model_struct.LocalGroup{},
&model_struct.LocalGroupMember{},
&model_struct.LocalGroupRequest{},
- &model_struct.LocalErrChatLog{},
&model_struct.LocalUser{},
&model_struct.LocalBlack{},
&model_struct.LocalConversation{},
&model_struct.NotificationSeqs{},
&model_struct.LocalChatLog{},
&model_struct.LocalAdminGroupRequest{},
- &model_struct.LocalWorkMomentsNotification{},
- &model_struct.LocalWorkMomentsNotificationUnreadCount{},
- &model_struct.TempCacheLocalChatLog{},
&model_struct.LocalChatLogReactionExtensions{},
&model_struct.LocalUpload{},
&model_struct.LocalStranger{},
diff --git a/pkg/db/db_interface/databse.go b/pkg/db/db_interface/databse.go
index b6ada51c0..f202d043e 100644
--- a/pkg/db/db_interface/databse.go
+++ b/pkg/db/db_interface/databse.go
@@ -30,11 +30,6 @@ type GroupModel interface {
GetGroups(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error)
GetGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error)
GetAllGroupInfoByGroupIDOrGroupName(ctx context.Context, keyword string, isSearchGroupID bool, isSearchGroupName bool) ([]*model_struct.LocalGroup, error)
- AddMemberCount(ctx context.Context, groupID string) error
- SubtractMemberCount(ctx context.Context, groupID string) error
- GetJoinedWorkingGroupIDList(ctx context.Context) ([]string, error)
- GetJoinedWorkingGroupList(ctx context.Context) ([]*model_struct.LocalGroup, error)
- GetUserJoinedGroupIDs(ctx context.Context, userID string) ([]string, error)
InsertAdminGroupRequest(ctx context.Context, groupRequest *model_struct.LocalAdminGroupRequest) error
DeleteAdminGroupRequest(ctx context.Context, groupID, userID string) error
@@ -44,59 +39,35 @@ type GroupModel interface {
DeleteGroupRequest(ctx context.Context, groupID, userID string) error
UpdateGroupRequest(ctx context.Context, groupRequest *model_struct.LocalGroupRequest) error
GetSendGroupApplication(ctx context.Context) ([]*model_struct.LocalGroupRequest, error)
- InsertSuperGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error
- DeleteAllSuperGroup(ctx context.Context) error
- GetSuperGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error)
- UpdateSuperGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error
- DeleteSuperGroup(ctx context.Context, groupID string) error
GetGroupMemberInfoByGroupIDUserID(ctx context.Context, groupID, userID string) (*model_struct.LocalGroupMember, error)
- GetAllGroupMemberList(ctx context.Context) ([]model_struct.LocalGroupMember, error)
- GetAllGroupMemberUserIDList(ctx context.Context) ([]model_struct.LocalGroupMember, error)
GetGroupMemberCount(ctx context.Context, groupID string) (int32, error)
GetGroupSomeMemberInfo(ctx context.Context, groupID string, userIDList []string) ([]*model_struct.LocalGroupMember, error)
- GetGroupAdminID(ctx context.Context, groupID string) ([]string, error)
GetGroupMemberListByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error)
GetGroupMemberListSplit(ctx context.Context, groupID string, filter int32, offset, count int) ([]*model_struct.LocalGroupMember, error)
GetGroupMemberListByUserIDs(ctx context.Context, groupID string, filter int32, userIDs []string) ([]*model_struct.LocalGroupMember, error)
GetGroupMemberOwnerAndAdminDB(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error)
- GetGroupMemberOwner(ctx context.Context, groupID string) (*model_struct.LocalGroupMember, error)
GetGroupMemberListSplitByJoinTimeFilter(ctx context.Context, groupID string, offset, count int, joinTimeBegin, joinTimeEnd int64, userIDList []string) ([]*model_struct.LocalGroupMember, error)
- GetGroupOwnerAndAdminByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error)
- GetGroupMemberUIDListByGroupID(ctx context.Context, groupID string) (result []string, err error)
- GetGroupMemberAllGroupIDs(ctx context.Context) ([]string, error)
InsertGroupMember(ctx context.Context, groupMember *model_struct.LocalGroupMember) error
BatchInsertGroupMember(ctx context.Context, groupMemberList []*model_struct.LocalGroupMember) error
DeleteGroupMember(ctx context.Context, groupID, userID string) error
DeleteGroupAllMembers(ctx context.Context, groupID string) error
UpdateGroupMember(ctx context.Context, groupMember *model_struct.LocalGroupMember) error
- UpdateGroupMemberField(ctx context.Context, groupID, userID string, args map[string]interface{}) error
- GetGroupMemberInfoIfOwnerOrAdmin(ctx context.Context) ([]*model_struct.LocalGroupMember, error)
SearchGroupMembersDB(ctx context.Context, keyword string, groupID string, isSearchMemberNickname, isSearchUserID bool, offset, count int) (result []*model_struct.LocalGroupMember, err error)
}
type MessageModel interface {
BatchInsertMessageList(ctx context.Context, conversationID string, MessageList []*model_struct.LocalChatLog) error
- // BatchInsertMessageListController(ctx context.Context, MessageList []*model_struct.LocalChatLog) error
InsertMessage(ctx context.Context, conversationID string, Message *model_struct.LocalChatLog) error
- // InsertMessageController(ctx context.Context, message *model_struct.LocalChatLog) error
- SearchMessageByKeyword(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error)
- // SearchMessageByKeywordController(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, sourceID string, startTime, endTime int64, sessionType, offset, count int) (result []*model_struct.LocalChatLog, err error)
- SearchMessageByContentType(ctx context.Context, contentType []int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error)
- // SearchMessageByContentTypeController(ctx context.Context, contentType []int, sourceID string, startTime, endTime int64, sessionType, offset, count int) (result []*model_struct.LocalChatLog, err error)
- SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error)
+ SearchMessageByKeyword(ctx context.Context, contentType []int, senderUserIDList []string, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error)
+ SearchMessageByContentType(ctx context.Context, contentType []int, senderUserIDList []string, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error)
+ SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, senderUserIDList []string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error)
GetMessage(ctx context.Context, conversationID, clientMsgID string) (*model_struct.LocalChatLog, error)
GetMessageBySeq(ctx context.Context, conversationID string, seq int64) (*model_struct.LocalChatLog, error)
UpdateColumnsMessage(ctx context.Context, conversationID string, ClientMsgID string, args map[string]interface{}) error
- // UpdateColumnsMessageController(ctx context.Context, ClientMsgID string, groupID string, sessionType int32, args map[string]interface{}) error
UpdateMessage(ctx context.Context, conversationID string, c *model_struct.LocalChatLog) error
UpdateMessageBySeq(ctx context.Context, conversationID string, c *model_struct.LocalChatLog) error
- // UpdateMessageStatusBySourceIDController(ctx context.Context, sourceID string, status, sessionType int32) error
UpdateMessageTimeAndStatus(ctx context.Context, conversationID, clientMsgID string, serverMsgID string, sendTime int64, status int32) error
- // UpdateMessageTimeAndStatusController(ctx context.Context, msg *sdk_struct.MsgStruct) error
- GetMessageList(ctx context.Context, conversationID string, count int, startTime int64, isReverse bool) (result []*model_struct.LocalChatLog, err error)
- // GetMessageListController(ctx context.Context, sourceID string, sessionType, count int, startTime int64, isReverse bool) (result []*model_struct.LocalChatLog, err error)
- GetMessageListNoTime(ctx context.Context, conversationID string, count int, isReverse bool) (result []*model_struct.LocalChatLog, err error)
- // GetMessageListNoTimeController(ctx context.Context, sourceID string, sessionType, count int, isReverse bool) (result []*model_struct.LocalChatLog, err error)
+ GetMessageList(ctx context.Context, conversationID string, count int, startTime, startSeq int64, startClientMsgID string, isReverse bool) (result []*model_struct.LocalChatLog, err error)
MarkConversationMessageAsReadDB(ctx context.Context, conversationID string, msgIDs []string) (rowsAffected int64, err error)
MarkConversationMessageAsReadBySeqs(ctx context.Context, conversationID string, seqs []int64) (rowsAffected int64, err error)
GetUnreadMessage(ctx context.Context, conversationID string) (result []*model_struct.LocalChatLog, err error)
@@ -104,28 +75,18 @@ type MessageModel interface {
GetMessagesByClientMsgIDs(ctx context.Context, conversationID string, msgIDs []string) (result []*model_struct.LocalChatLog, err error)
GetMessagesBySeqs(ctx context.Context, conversationID string, seqs []int64) (result []*model_struct.LocalChatLog, err error)
GetConversationNormalMsgSeq(ctx context.Context, conversationID string) (int64, error)
- GetConversationNormalMsgSeqNoInit(ctx context.Context, conversationID string) (int64, error)
+ CheckConversationNormalMsgSeq(ctx context.Context, conversationID string) (int64, error)
GetConversationPeerNormalMsgSeq(ctx context.Context, conversationID string) (int64, error)
+ GetLatestActiveMessage(ctx context.Context, conversationID string, isReverse bool) (result []*model_struct.LocalChatLog, err error)
+ GetLatestValidServerMessage(ctx context.Context, conversationID string, startTime int64, isReverse bool) (*model_struct.LocalChatLog, error)
- UpdateMsgSenderNickname(ctx context.Context, sendID, nickname string, sType int) error
- UpdateMsgSenderFaceURL(ctx context.Context, sendID, faceURL string, sType int) error
- // UpdateMsgSenderFaceURLAndSenderNicknameController(ctx context.Context, sendID, faceURL, nickname string, sessionType int) error
UpdateMsgSenderFaceURLAndSenderNickname(ctx context.Context, conversationID, sendID, faceURL, nickname string) error
- GetAbnormalMsgSeq(ctx context.Context) (int64, error)
- GetAbnormalMsgSeqList(ctx context.Context) ([]int64, error)
- BatchInsertExceptionMsg(ctx context.Context, MessageList []*model_struct.LocalErrChatLog) error
- GetConversationAbnormalMsgSeq(ctx context.Context, groupID string) (int64, error)
- BatchInsertTempCacheMessageList(ctx context.Context, MessageList []*model_struct.TempCacheLocalChatLog) error
- InsertTempCacheMessage(ctx context.Context, Message *model_struct.TempCacheLocalChatLog) error
DeleteConversationAllMessages(ctx context.Context, conversationID string) error
MarkDeleteConversationAllMessages(ctx context.Context, conversationID string) error
- GetAlreadyExistSeqList(ctx context.Context, conversationID string, lostSeqList []int64) (seqList []int64, err error)
-
BatchInsertConversationUnreadMessageList(ctx context.Context, messageList []*model_struct.LocalConversationUnreadMessage) error
DeleteConversationUnreadMessageList(ctx context.Context, conversationID string, sendTime int64) int64
DeleteConversationMsgs(ctx context.Context, conversationID string, msgIDs []string) error
- // DeleteConversationMsgsBySeqs(ctx context.Context, conversationID string, seqs []int64) error
SetNotificationSeq(ctx context.Context, conversationID string, seq int64) error
BatchInsertNotificationSeq(ctx context.Context, notificationSeqs []*model_struct.NotificationSeqs) error
GetNotificationAllSeqs(ctx context.Context) ([]*model_struct.NotificationSeqs, error)
@@ -134,6 +95,7 @@ type MessageModel interface {
type ConversationModel interface {
GetConversationByUserID(ctx context.Context, userID string) (*model_struct.LocalConversation, error)
GetAllConversationListDB(ctx context.Context) ([]*model_struct.LocalConversation, error)
+ FindAllUnreadConversationConversationID(ctx context.Context) ([]string, error)
GetHiddenConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error)
GetAllConversations(ctx context.Context) ([]*model_struct.LocalConversation, error)
GetAllSingleConversationIDList(ctx context.Context) (result []string, err error)
@@ -171,8 +133,6 @@ type UserModel interface {
UpdateLoginUser(ctx context.Context, user *model_struct.LocalUser) error
UpdateLoginUserByMap(ctx context.Context, user *model_struct.LocalUser, args map[string]interface{}) error
InsertLoginUser(ctx context.Context, user *model_struct.LocalUser) error
- GetStrangerInfo(ctx context.Context, userIDs []string) ([]*model_struct.LocalStranger, error)
- SetStrangerInfo(ctx context.Context, localStrangerList []*model_struct.LocalStranger) error
ProcessUserCommandAdd(ctx context.Context, command *model_struct.LocalUserCommand) error
ProcessUserCommandUpdate(ctx context.Context, command *model_struct.LocalUserCommand) error
ProcessUserCommandDelete(ctx context.Context, command *model_struct.LocalUserCommand) error
@@ -210,16 +170,6 @@ type FriendModel interface {
DeleteBlack(ctx context.Context, blockUserID string) error
}
-type ReactionModel interface {
- GetMessageReactionExtension(ctx context.Context, msgID string) (result *model_struct.LocalChatLogReactionExtensions, err error)
- InsertMessageReactionExtension(ctx context.Context, messageReactionExtension *model_struct.LocalChatLogReactionExtensions) error
- UpdateMessageReactionExtension(ctx context.Context, c *model_struct.LocalChatLogReactionExtensions) error
- // GetAndUpdateMessageReactionExtension(ctx context.Context, msgID string, m map[string]*sdkws.KeyValue) error
- // DeleteAndUpdateMessageReactionExtension(ctx context.Context, msgID string, m map[string]*sdkws.KeyValue) error
- GetMultipleMessageReactionExtension(ctx context.Context, msgIDList []string) (result []*model_struct.LocalChatLogReactionExtensions, err error)
- DeleteMessageReactionExtension(ctx context.Context, msgID string) error
-}
-
type S3Model interface {
GetUpload(ctx context.Context, partHash string) (*model_struct.LocalUpload, error)
InsertUpload(ctx context.Context, upload *model_struct.LocalUpload) error
@@ -254,7 +204,6 @@ type DataBase interface {
ConversationModel
UserModel
FriendModel
- ReactionModel
S3Model
SendingMessagesModel
VersionSyncModel
diff --git a/pkg/db/db_js.go b/pkg/db/db_js.go
index f80cf4119..61ddcf753 100644
--- a/pkg/db/db_js.go
+++ b/pkg/db/db_js.go
@@ -28,19 +28,15 @@ type IndexDB struct {
*indexdb.LocalUsers
*indexdb.LocalConversations
*indexdb.LocalChatLogs
- *indexdb.LocalSuperGroup
*indexdb.LocalConversationUnreadMessages
*indexdb.LocalGroups
*indexdb.LocalGroupMember
- *indexdb.LocalCacheMessage
*indexdb.FriendRequest
*indexdb.Black
*indexdb.Friend
*indexdb.LocalGroupRequest
- *indexdb.LocalChatLogReactionExtensions
*indexdb.NotificationSeqs
*indexdb.LocalUpload
- *indexdb.LocalStrangers
*indexdb.LocalSendingMessages
*indexdb.LocalUserCommand
*indexdb.LocalVersionSync
@@ -64,19 +60,15 @@ func NewDataBase(ctx context.Context, loginUserID string, dbDir string, logLevel
LocalUsers: indexdb.NewLocalUsers(),
LocalConversations: indexdb.NewLocalConversations(),
LocalChatLogs: indexdb.NewLocalChatLogs(loginUserID),
- LocalSuperGroup: indexdb.NewLocalSuperGroup(),
LocalConversationUnreadMessages: indexdb.NewLocalConversationUnreadMessages(),
LocalGroups: indexdb.NewLocalGroups(),
LocalGroupMember: indexdb.NewLocalGroupMember(),
- LocalCacheMessage: indexdb.NewLocalCacheMessage(),
FriendRequest: indexdb.NewFriendRequest(loginUserID),
Black: indexdb.NewBlack(loginUserID),
Friend: indexdb.NewFriend(loginUserID),
LocalGroupRequest: indexdb.NewLocalGroupRequest(),
- LocalChatLogReactionExtensions: indexdb.NewLocalChatLogReactionExtensions(),
NotificationSeqs: indexdb.NewNotificationSeqs(),
LocalUpload: indexdb.NewLocalUpload(),
- LocalStrangers: indexdb.NewLocalStrangers(),
LocalSendingMessages: indexdb.NewLocalSendingMessages(),
LocalUserCommand: indexdb.NewLocalUserCommand(),
LocalVersionSync: indexdb.NewLocalVersionSync(),
diff --git a/pkg/db/err_chat_log_model.go b/pkg/db/err_chat_log_model.go
deleted file mode 100644
index 9c2a88097..000000000
--- a/pkg/db/err_chat_log_model.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/tools/errs"
-)
-
-func (d *DataBase) initSuperLocalErrChatLog(ctx context.Context, groupID string) {
- if !d.conn.WithContext(ctx).Migrator().HasTable(utils.GetErrTableName(groupID)) {
- d.conn.WithContext(ctx).Table(utils.GetErrTableName(groupID)).AutoMigrate(&model_struct.LocalErrChatLog{})
- }
-}
-func (d *DataBase) SuperBatchInsertExceptionMsg(ctx context.Context, MessageList []*model_struct.LocalErrChatLog, groupID string) error {
- if MessageList == nil {
- return nil
- }
- d.initSuperLocalErrChatLog(ctx, groupID)
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Create(MessageList).Error, "BatchInsertMessageList failed")
-}
-func (d *DataBase) GetAbnormalMsgSeq(ctx context.Context) (int64, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seq int64
- return seq, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalErrChatLog{}).Select("IFNULL(max(seq),0)").Find(&seq).Error, "GetAbnormalMsgSeq")
-}
-func (d *DataBase) GetAbnormalMsgSeqList(ctx context.Context) ([]int64, error) {
- var seqList []int64
- return seqList, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalErrChatLog{}).Select("seq").Find(&seqList).Error, "GetAbnormalMsgSeqList")
-}
-func (d *DataBase) BatchInsertExceptionMsg(ctx context.Context, messageList []*model_struct.LocalErrChatLog) error {
- if messageList == nil {
- return nil
- }
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Create(messageList).Error, "BatchInsertMessageList failed")
-}
-func (d *DataBase) BatchInsertExceptionMsgController(ctx context.Context, messageList []*model_struct.LocalErrChatLog) error {
- if len(messageList) == 0 {
- return nil
- }
- switch messageList[len(messageList)-1].SessionType {
- case constant.SuperGroupChatType:
- return d.SuperBatchInsertExceptionMsg(ctx, messageList, messageList[len(messageList)-1].RecvID)
- default:
- return d.BatchInsertExceptionMsg(ctx, messageList)
- }
-}
-func (d *DataBase) GetConversationAbnormalMsgSeq(ctx context.Context, conversationID string) (int64, error) {
- var seq int64
- if !d.conn.WithContext(ctx).Migrator().HasTable(utils.GetErrTableName(conversationID)) {
- return 0, nil
- }
- return seq, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetErrTableName(conversationID)).Select("IFNULL(max(seq),0)").Find(&seq).Error, "GetConversationNormalMsgSeq")
-}
diff --git a/pkg/db/friend_model.go b/pkg/db/friend_model.go
index f43c6382c..de20ae23a 100644
--- a/pkg/db/friend_model.go
+++ b/pkg/db/friend_model.go
@@ -29,27 +29,27 @@ import (
)
func (d *DataBase) InsertFriend(ctx context.Context, friend *model_struct.LocalFriend) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(friend).Error, "InsertFriend failed")
}
func (d *DataBase) DeleteFriendDB(ctx context.Context, friendUserID string) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id=? and friend_user_id=?", d.loginUserID, friendUserID).Delete(&model_struct.LocalFriend{}).Error, "DeleteFriend failed")
}
func (d *DataBase) GetFriendListCount(ctx context.Context) (int64, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var count int64
return count, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalFriend{}).Count(&count).Error, "GetFriendListCount failed")
}
func (d *DataBase) UpdateFriend(ctx context.Context, friend *model_struct.LocalFriend) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(friend).Select("*").Updates(*friend)
if t.RowsAffected == 0 {
@@ -59,16 +59,16 @@ func (d *DataBase) UpdateFriend(ctx context.Context, friend *model_struct.LocalF
}
func (d *DataBase) GetAllFriendList(ctx context.Context) ([]*model_struct.LocalFriend, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendList []*model_struct.LocalFriend
return friendList, errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id = ?", d.loginUserID).Find(&friendList).Error,
"GetFriendList failed")
}
func (d *DataBase) GetPageFriendList(ctx context.Context, offset, count int) ([]*model_struct.LocalFriend, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendList []*model_struct.LocalFriend
err := errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id = ?", d.loginUserID).Offset(offset).Limit(count).Order("name").Find(&friendList).Error,
"GetFriendList failed")
@@ -76,8 +76,8 @@ func (d *DataBase) GetPageFriendList(ctx context.Context, offset, count int) ([]
}
func (d *DataBase) BatchInsertFriend(ctx context.Context, friendList []*model_struct.LocalFriend) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
if friendList == nil {
return errs.New("nil").Wrap()
}
@@ -85,14 +85,14 @@ func (d *DataBase) BatchInsertFriend(ctx context.Context, friendList []*model_st
}
func (d *DataBase) DeleteAllFriend(ctx context.Context) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model_struct.LocalFriend{}).Error, "DeleteAllFriend failed")
}
func (d *DataBase) SearchFriendList(ctx context.Context, keyword string, isSearchUserID, isSearchNickname, isSearchRemark bool) ([]*model_struct.LocalFriend, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var count int
var friendList []*model_struct.LocalFriend
var condition string
@@ -118,16 +118,16 @@ func (d *DataBase) SearchFriendList(ctx context.Context, keyword string, isSearc
}
func (d *DataBase) GetFriendInfoByFriendUserID(ctx context.Context, FriendUserID string) (*model_struct.LocalFriend, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friend model_struct.LocalFriend
return &friend, errs.WrapMsg(d.conn.WithContext(ctx).Where("owner_user_id = ? AND friend_user_id = ?",
d.loginUserID, FriendUserID).Take(&friend).Error, "GetFriendInfoByFriendUserID failed")
}
func (d *DataBase) GetFriendInfoList(ctx context.Context, friendUserIDList []string) ([]*model_struct.LocalFriend, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendList []*model_struct.LocalFriend
err := errs.WrapMsg(d.conn.WithContext(ctx).Where("friend_user_id IN ?", friendUserIDList).Find(&friendList).Error, "GetFriendInfoListByFriendUserID failed")
return friendList, err
diff --git a/testv2/friend_db_test.go b/pkg/db/friend_model_test.go
similarity index 90%
rename from testv2/friend_db_test.go
rename to pkg/db/friend_model_test.go
index c98638bb8..9e55189af 100644
--- a/testv2/friend_db_test.go
+++ b/pkg/db/friend_model_test.go
@@ -1,17 +1,16 @@
-package testv2
+package db
import (
"context"
"testing"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/tools/log"
)
func Test_GetFriendListCount(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -25,7 +24,7 @@ func Test_GetFriendListCount(t *testing.T) {
func Test_BatchInsertFriend(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -79,7 +78,7 @@ func Test_BatchInsertFriend(t *testing.T) {
func Test_DeleteAllFriend(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
diff --git a/pkg/db/friend_request_model.go b/pkg/db/friend_request_model.go
index 2bf0dbbb6..255412c9c 100644
--- a/pkg/db/friend_request_model.go
+++ b/pkg/db/friend_request_model.go
@@ -26,20 +26,20 @@ import (
)
func (d *DataBase) InsertFriendRequest(ctx context.Context, friendRequest *model_struct.LocalFriendRequest) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(friendRequest).Error, "InsertFriendRequest failed")
}
func (d *DataBase) DeleteFriendRequestBothUserID(ctx context.Context, fromUserID, toUserID string) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("from_user_id=? and to_user_id=?", fromUserID, toUserID).Delete(&model_struct.LocalFriendRequest{}).Error, "DeleteFriendRequestBothUserID failed")
}
func (d *DataBase) UpdateFriendRequest(ctx context.Context, friendRequest *model_struct.LocalFriendRequest) error {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(friendRequest).Select("*").Updates(*friendRequest)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -48,29 +48,29 @@ func (d *DataBase) UpdateFriendRequest(ctx context.Context, friendRequest *model
}
func (d *DataBase) GetRecvFriendApplication(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendRequestList []*model_struct.LocalFriendRequest
return friendRequestList, errs.WrapMsg(d.conn.WithContext(ctx).Where("to_user_id = ?", d.loginUserID).Order("create_time DESC").Find(&friendRequestList).Error, "GetRecvFriendApplication failed")
}
func (d *DataBase) GetSendFriendApplication(ctx context.Context) ([]*model_struct.LocalFriendRequest, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendRequestList []*model_struct.LocalFriendRequest
return friendRequestList, errs.WrapMsg(d.conn.WithContext(ctx).Where("from_user_id = ?", d.loginUserID).Order("create_time DESC").Find(&friendRequestList).Error, "GetSendFriendApplication failed")
}
func (d *DataBase) GetFriendApplicationByBothID(ctx context.Context, fromUserID, toUserID string) (*model_struct.LocalFriendRequest, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var friendRequest model_struct.LocalFriendRequest
return &friendRequest, errs.WrapMsg(d.conn.WithContext(ctx).Where("from_user_id = ? AND to_user_id = ?", fromUserID, toUserID).Take(&friendRequest).Error, "GetFriendApplicationByBothID failed")
}
func (d *DataBase) GetBothFriendReq(ctx context.Context, fromUserID, toUserID string) (friendRequests []*model_struct.LocalFriendRequest, err error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
return friendRequests, errs.WrapMsg(d.conn.WithContext(ctx).Where("(from_user_id = ? AND to_user_id = ?) OR (from_user_id = ? AND to_user_id = ?)", fromUserID, toUserID, toUserID, fromUserID).Find(&friendRequests).Error, "GetFriendApplicationByBothID failed")
}
diff --git a/pkg/db/group_member_model.go b/pkg/db/group_member_model.go
index d6f963bf2..0d1fcb006 100644
--- a/pkg/db/group_member_model.go
+++ b/pkg/db/group_member_model.go
@@ -28,59 +28,47 @@ import (
)
func (d *DataBase) GetGroupMemberInfoByGroupIDUserID(ctx context.Context, groupID, userID string) (*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMember model_struct.LocalGroupMember
return &groupMember, errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id = ? AND user_id = ?",
groupID, userID).Take(&groupMember).Error, "GetGroupMemberInfoByGroupIDUserID failed")
}
func (d *DataBase) GetAllGroupMemberList(ctx context.Context) ([]model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- var groupMemberList []model_struct.LocalGroupMember
- return groupMemberList, errs.WrapMsg(d.conn.WithContext(ctx).Find(&groupMemberList).Error, "GetAllGroupMemberList failed")
-}
-func (d *DataBase) GetAllGroupMemberUserIDList(ctx context.Context) ([]model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []model_struct.LocalGroupMember
return groupMemberList, errs.WrapMsg(d.conn.WithContext(ctx).Find(&groupMemberList).Error, "GetAllGroupMemberList failed")
}
func (d *DataBase) GetGroupMemberCount(ctx context.Context, groupID string) (int32, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var count int64
err := d.conn.WithContext(ctx).Model(&model_struct.LocalGroupMember{}).Where("group_id = ? ", groupID).Count(&count).Error
return int32(count), errs.WrapMsg(err, "GetGroupMemberCount failed")
}
func (d *DataBase) GetGroupSomeMemberInfo(ctx context.Context, groupID string, userIDList []string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
- err := d.conn.WithContext(ctx).Where("group_id = ? And user_id IN ? ", groupID, userIDList).Find(&groupMemberList).Error
+ err := d.conn.WithContext(ctx).Where("group_id = ? AND user_id IN ? ", groupID, userIDList).Find(&groupMemberList).Error
return groupMemberList, errs.WrapMsg(err, "GetGroupMemberListByGroupID failed ")
}
-func (d *DataBase) GetGroupAdminID(ctx context.Context, groupID string) ([]string, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- var adminIDList []string
- return adminIDList, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalGroupMember{}).Select("user_id").Where("group_id = ? And role_level = ?", groupID, constant.GroupAdmin).Find(&adminIDList).Error, "")
-}
func (d *DataBase) GetGroupMemberListByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
err := d.conn.WithContext(ctx).Where("group_id = ? ", groupID).Find(&groupMemberList).Error
return groupMemberList, errs.WrapMsg(err, "GetGroupMemberListByGroupID failed ")
}
func (d *DataBase) GetGroupMemberListByUserIDs(ctx context.Context, groupID string, filter int32, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
var err error
switch filter {
@@ -105,8 +93,8 @@ func (d *DataBase) GetGroupMemberListByUserIDs(ctx context.Context, groupID stri
}
func (d *DataBase) GetGroupMemberListSplit(ctx context.Context, groupID string, filter int32, offset, count int) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
var err error
switch filter {
@@ -130,25 +118,17 @@ func (d *DataBase) GetGroupMemberListSplit(ctx context.Context, groupID string,
}
func (d *DataBase) GetGroupMemberOwnerAndAdminDB(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
err := d.conn.WithContext(ctx).Where("group_id = ? And (role_level = ? OR role_level = ?)", groupID, constant.GroupOwner, constant.GroupAdmin).Order("join_time DESC").Find(&groupMemberList).Error
return groupMemberList, errs.WrapMsg(err, "GetGroupMemberListSplit failed ")
}
-func (d *DataBase) GetGroupMemberOwner(ctx context.Context, groupID string) (*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- var groupMember model_struct.LocalGroupMember
- err := d.conn.WithContext(ctx).Where("group_id = ? And role_level = ?", groupID, constant.GroupOwner).Find(&groupMember).Error
- return &groupMember, errs.WrapMsg(err, "GetGroupMemberListSplit failed ")
-}
-
func (d *DataBase) GetGroupMemberListSplitByJoinTimeFilter(ctx context.Context, groupID string, offset, count int, joinTimeBegin, joinTimeEnd int64, userIDList []string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupMemberList []*model_struct.LocalGroupMember
var err error
if len(userIDList) == 0 {
@@ -159,61 +139,35 @@ func (d *DataBase) GetGroupMemberListSplitByJoinTimeFilter(ctx context.Context,
return groupMemberList, errs.WrapMsg(err, "GetGroupMemberListSplitByJoinTimeFilter failed ")
}
-func (d *DataBase) GetGroupOwnerAndAdminByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- var groupMemberList []*model_struct.LocalGroupMember
- err := d.conn.WithContext(ctx).Where("group_id = ? AND (role_level = ? Or role_level = ?)", groupID, constant.GroupOwner, constant.GroupAdmin).Find(&groupMemberList).Error
- return groupMemberList, errs.WrapMsg(err, "GetGroupMemberListByGroupID failed ")
-}
-
-func (d *DataBase) GetGroupMemberUIDListByGroupID(ctx context.Context, groupID string) (result []string, err error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- var g model_struct.LocalGroupMember
- g.GroupID = groupID
- err = d.conn.WithContext(ctx).Model(&g).Where("group_id = ?", groupID).Pluck("user_id", &result).Error
- return result, errs.WrapMsg(err, "GetGroupMemberListByGroupID failed ")
-}
-
func (d *DataBase) InsertGroupMember(ctx context.Context, groupMember *model_struct.LocalGroupMember) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupMember).Error, "")
}
-//funcation (d *DataBase) BatchInsertMessageList(ctx context.Context, MessageList []*model_struct.LocalChatLog) error {
-// if MessageList == nil {
-// return nil
-// }
-// d.mRWMutex.Lock()
-// defer d.mRWMutex.Unlock()
-// return errs.WrapMsg(d.conn.WithContext(ctx).Create(MessageList).Error, "BatchInsertMessageList failed")
-//}
-
func (d *DataBase) BatchInsertGroupMember(ctx context.Context, groupMemberList []*model_struct.LocalGroupMember) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupMemberList).Error, "BatchInsertGroupMember failed")
}
func (d *DataBase) DeleteGroupMember(ctx context.Context, groupID, userID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
var groupMember model_struct.LocalGroupMember
return d.conn.WithContext(ctx).Where("group_id=? and user_id=?", groupID, userID).Delete(&groupMember).Error
}
func (d *DataBase) DeleteGroupAllMembers(ctx context.Context, groupID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
var groupMember model_struct.LocalGroupMember
return d.conn.WithContext(ctx).Where("group_id=? ", groupID).Delete(&groupMember).Error
}
func (d *DataBase) UpdateGroupMember(ctx context.Context, groupMember *model_struct.LocalGroupMember) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(groupMember).Select("*").Updates(*groupMember)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -221,36 +175,9 @@ func (d *DataBase) UpdateGroupMember(ctx context.Context, groupMember *model_str
return errs.WrapMsg(t.Error, "")
}
-func (d *DataBase) UpdateGroupMemberField(ctx context.Context, groupID, userID string, args map[string]interface{}) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- c := model_struct.LocalGroupMember{GroupID: groupID, UserID: userID}
- t := d.conn.WithContext(ctx).Model(&c).Updates(args)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateGroupMemberField failed")
-}
-
-func (d *DataBase) GetGroupMemberInfoIfOwnerOrAdmin(ctx context.Context) ([]*model_struct.LocalGroupMember, error) {
- var ownerAndAdminList []*model_struct.LocalGroupMember
- groupList, err := d.GetJoinedGroupListDB(ctx)
- if err != nil {
- return nil, errs.Wrap(err)
- }
- for _, v := range groupList {
- memberList, err := d.GetGroupOwnerAndAdminByGroupID(ctx, v.GroupID)
- if err != nil {
- return nil, errs.Wrap(err)
- }
- ownerAndAdminList = append(ownerAndAdminList, memberList...)
- }
- return ownerAndAdminList, nil
-}
-
func (d *DataBase) SearchGroupMembersDB(ctx context.Context, keyword string, groupID string, isSearchMemberNickname, isSearchUserID bool, offset, count int) (result []*model_struct.LocalGroupMember, err error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
if !isSearchMemberNickname && !isSearchUserID {
return nil, errors.New("args failed")
}
@@ -284,21 +211,3 @@ func (d *DataBase) SearchGroupMembersDB(ctx context.Context, keyword string, gro
}
return result, err
}
-
-func (d *DataBase) GetGroupMemberAllGroupIDs(ctx context.Context) ([]string, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- // SELECT DISTINCT group_id FROM local_group_members;
- var groupIDs []string
- err := d.conn.WithContext(ctx).Select("DISTINCT group_id").Model(&model_struct.LocalGroupMember{}).Pluck("group_id", &groupIDs).Error
- if err != nil {
- return nil, err
- }
- return groupIDs, nil
-}
-
-func (d *DataBase) GetUserJoinedGroupIDs(ctx context.Context, userID string) (groupIDs []string, err error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- return groupIDs, d.conn.WithContext(ctx).Model(&model_struct.LocalGroupMember{}).Where("user_id = ?", userID).Pluck("group_id", &groupIDs).Error
-}
diff --git a/pkg/db/group_model.go b/pkg/db/group_model.go
index 4153c2674..081b12a49 100644
--- a/pkg/db/group_model.go
+++ b/pkg/db/group_model.go
@@ -29,19 +29,21 @@ import (
)
func (d *DataBase) InsertGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupInfo).Error, "InsertGroup failed")
}
+
func (d *DataBase) DeleteGroup(ctx context.Context, groupID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
localGroup := model_struct.LocalGroup{GroupID: groupID}
return errs.WrapMsg(d.conn.WithContext(ctx).Delete(&localGroup).Error, "DeleteGroup failed")
}
+
func (d *DataBase) UpdateGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(groupInfo).Select("*").Updates(*groupInfo)
if t.RowsAffected == 0 {
@@ -49,43 +51,45 @@ func (d *DataBase) UpdateGroup(ctx context.Context, groupInfo *model_struct.Loca
}
return errs.Wrap(t.Error)
}
+
func (d *DataBase) BatchInsertGroup(ctx context.Context, groupList []*model_struct.LocalGroup) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupList).Error, "BatchInsertGroup failed")
}
func (d *DataBase) DeleteAllGroup(ctx context.Context) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model_struct.LocalGroup{}).Error, "DeleteAllGroup failed")
}
func (d *DataBase) GetJoinedGroupListDB(ctx context.Context) ([]*model_struct.LocalGroup, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupList []*model_struct.LocalGroup
err := d.conn.WithContext(ctx).Find(&groupList).Error
return groupList, errs.WrapMsg(err, "GetJoinedGroupList failed ")
}
func (d *DataBase) GetGroups(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupList []*model_struct.LocalGroup
err := d.conn.WithContext(ctx).Where("group_id in (?)", groupIDs).Find(&groupList).Error
return groupList, errs.WrapMsg(err, "GetGroups failed ")
}
func (d *DataBase) GetGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var g model_struct.LocalGroup
return &g, errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id = ?", groupID).Take(&g).Error, "GetGroupList failed")
}
+
func (d *DataBase) GetAllGroupInfoByGroupIDOrGroupName(ctx context.Context, keyword string, isSearchGroupID bool, isSearchGroupName bool) ([]*model_struct.LocalGroup, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupList []*model_struct.LocalGroup
var condition string
@@ -101,17 +105,3 @@ func (d *DataBase) GetAllGroupInfoByGroupIDOrGroupName(ctx context.Context, keyw
err := d.conn.WithContext(ctx).Where(condition).Order("create_time DESC").Find(&groupList).Error
return groupList, errs.WrapMsg(err, "GetAllGroupInfoByGroupIDOrGroupName failed ")
}
-
-func (d *DataBase) AddMemberCount(ctx context.Context, groupID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- group := model_struct.LocalGroup{GroupID: groupID}
- return errs.WrapMsg(d.conn.WithContext(ctx).Model(&group).Updates(map[string]interface{}{"member_count": gorm.Expr("member_count+1")}).Error, "")
-}
-
-func (d *DataBase) SubtractMemberCount(ctx context.Context, groupID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
- group := model_struct.LocalGroup{GroupID: groupID}
- return errs.WrapMsg(d.conn.WithContext(ctx).Model(&group).Updates(map[string]interface{}{"member_count": gorm.Expr("member_count-1")}).Error, "")
-}
diff --git a/testv2/group_db_test.go b/pkg/db/group_model_test.go
similarity index 83%
rename from testv2/group_db_test.go
rename to pkg/db/group_model_test.go
index e26ffa471..4d9b08c0a 100644
--- a/testv2/group_db_test.go
+++ b/pkg/db/group_model_test.go
@@ -1,16 +1,15 @@
-package testv2
+package db
import (
"context"
"testing"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
)
func Test_GetGroupMemberListByUserIDs(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -25,7 +24,7 @@ func Test_GetGroupMemberListByUserIDs(t *testing.T) {
func Test_BatchInsertGroup(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -34,7 +33,7 @@ func Test_BatchInsertGroup(t *testing.T) {
localGroups := []*model_struct.LocalGroup{
{
GroupID: "1234567",
- GroupName: "测试1234",
+ GroupName: "test1234",
Notification: "",
Introduction: "",
FaceURL: "",
@@ -54,9 +53,9 @@ func Test_BatchInsertGroup(t *testing.T) {
},
{
GroupID: "1234568",
- GroupName: "测试5678",
- Notification: "新的通知",
- Introduction: "这是一个测试组",
+ GroupName: "test5678",
+ Notification: "New Notification",
+ Introduction: "This is a test group",
FaceURL: "https://example.com/face.png",
CreateTime: 1666777420,
Status: 1,
@@ -82,7 +81,7 @@ func Test_BatchInsertGroup(t *testing.T) {
func Test_DeleteAllGroup(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
diff --git a/pkg/db/group_request_model.go b/pkg/db/group_request_model.go
index 856175f0b..6d2cac141 100644
--- a/pkg/db/group_request_model.go
+++ b/pkg/db/group_request_model.go
@@ -26,18 +26,18 @@ import (
)
func (d *DataBase) InsertGroupRequest(ctx context.Context, groupRequest *model_struct.LocalGroupRequest) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(groupRequest).Error, "InsertGroupRequest failed")
}
func (d *DataBase) DeleteGroupRequest(ctx context.Context, groupID, userID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("group_id=? and user_id=?", groupID, userID).Delete(&model_struct.LocalGroupRequest{}).Error, "DeleteGroupRequest failed")
}
func (d *DataBase) UpdateGroupRequest(ctx context.Context, groupRequest *model_struct.LocalGroupRequest) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(groupRequest).Select("*").Updates(*groupRequest)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -46,8 +46,8 @@ func (d *DataBase) UpdateGroupRequest(ctx context.Context, groupRequest *model_s
}
func (d *DataBase) GetSendGroupApplication(ctx context.Context) ([]*model_struct.LocalGroupRequest, error) {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var groupRequestList []*model_struct.LocalGroupRequest
return groupRequestList, errs.Wrap(d.conn.WithContext(ctx).Order("create_time DESC").Find(&groupRequestList).Error)
}
diff --git a/pkg/db/model_struct/data_model_struct.go b/pkg/db/model_struct/data_model_struct.go
index d3f4e3348..a1308e29a 100644
--- a/pkg/db/model_struct/data_model_struct.go
+++ b/pkg/db/model_struct/data_model_struct.go
@@ -21,34 +21,6 @@ import (
"github.com/openimsdk/tools/errs"
)
-//
-//message FriendInfo{
-//string OwnerUserID = 1;
-//string Remark = 2;
-//int64 CreateTime = 3;
-//UserInfo FriendUser = 4;
-//int32 AddSource = 5;
-//string OperatorUserID = 6;
-//string Ex = 7;
-//}
-//open_im_sdk.FriendInfo(FriendUser) != imdb.Friend(FriendUserID)
-// table = ` CREATE TABLE IF NOT EXISTS friends(
-// owner_user_id CHAR (64) NOT NULL,
-// friend_user_id CHAR (64) NOT NULL ,
-// name varchar(64) DEFAULT NULL ,
-// face_url varchar(100) DEFAULT NULL ,
-// remark varchar(255) DEFAULT NULL,
-// gender int DEFAULT NULL ,
-// phone_number varchar(32) DEFAULT NULL ,
-// birth INTEGER DEFAULT NULL ,
-// email varchar(64) DEFAULT NULL ,
-// create_time INTEGER DEFAULT NULL ,
-// add_source int DEFAULT NULL ,
-// operator_user_id CHAR(64) DEFAULT NULL,
-// ex varchar(1024) DEFAULT NULL,
-// PRIMARY KEY (owner_user_id,friend_user_id)
-// )`
-
type LocalFriend struct {
OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:varchar(64)" json:"ownerUserID"`
FriendUserID string `gorm:"column:friend_user_id;primary_key;type:varchar(64)" json:"userID"`
@@ -67,18 +39,6 @@ func (LocalFriend) TableName() string {
return "local_friends"
}
-// message FriendRequest{
-// string FromUserID = 1;
-// string ToUserID = 2;
-// int32 HandleResult = 3;
-// string ReqMsg = 4;
-// int64 CreateTime = 5;
-// string HandlerUserID = 6;
-// string HandleMsg = 7;
-// int64 HandleTime = 8;
-// string Ex = 9;
-// }
-// open_im_sdk.FriendRequest == imdb.FriendRequest
type LocalFriendRequest struct {
FromUserID string `gorm:"column:from_user_id;primary_key;type:varchar(64)" json:"fromUserID"`
FromNickname string `gorm:"column:from_nickname;type:varchar;type:varchar(255)" json:"fromNickname"`
@@ -101,34 +61,6 @@ type LocalFriendRequest struct {
AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
}
-//message GroupInfo{
-// string GroupID = 1;
-// string GroupName = 2;
-// string NotificationCmd = 3;
-// string Introduction = 4;
-// string FaceUrl = 5;
-// string OwnerUserID = 6;
-// uint32 MemberCount = 8;
-// int64 CreateTime = 7;
-// string Ex = 9;
-// int32 Status = 10;
-// string CreatorUserID = 11;
-// int32 GroupType = 12;
-//}
-// open_im_sdk.GroupInfo (OwnerUserID , MemberCount )> imdb.Group
-// group_id char(64) NOT NULL,
-// name varchar(64) DEFAULT NULL ,
-// introduction varchar(255) DEFAULT NULL,
-// notification varchar(255) DEFAULT NULL,
-// face_url varchar(100) DEFAULT NULL,
-// group_type int DEFAULT NULL,
-// status int DEFAULT NULL,
-// creator_user_id char(64) DEFAULT NULL,
-// create_time INTEGER DEFAULT NULL,
-// ex varchar(1024) DEFAULT NULL,
-// PRIMARY KEY (group_id)
-// )`
-
type LocalGroup struct {
GroupID string `gorm:"column:group_id;primary_key;type:varchar(64)" json:"groupID" binding:"required"`
GroupName string `gorm:"column:name;size:255" json:"groupName"`
@@ -154,27 +86,6 @@ func (LocalGroup) TableName() string {
return "local_groups"
}
-//message GroupMemberFullInfo {
-//string GroupID = 1 ;
-//string UserID = 2 ;
-//int32 roleLevel = 3;
-//int64 JoinTime = 4;
-//string NickName = 5;
-//string FaceUrl = 6;
-//int32 JoinSource = 8;
-//string OperatorUserID = 9;
-//string Ex = 10;
-//int32 AppMangerLevel = 7; //if >0
-//} open_im_sdk.GroupMemberFullInfo(AppMangerLevel) > imdb.GroupMember
-// group_id char(64) NOT NULL,
-// user_id char(64) NOT NULL,
-// nickname varchar(64) DEFAULT NULL,
-// user_group_face_url varchar(64) DEFAULT NULL,
-// role_level int DEFAULT NULL,
-// join_time INTEGER DEFAULT NULL,
-// join_source int DEFAULT NULL,
-// operator_user_id char(64) NOT NULL,
-
type LocalGroupMember struct {
GroupID string `gorm:"column:group_id;primary_key;type:varchar(64)" json:"groupID"`
UserID string `gorm:"column:user_id;primary_key;type:varchar(64)" json:"userID"`
@@ -194,17 +105,6 @@ func (LocalGroupMember) TableName() string {
return "local_group_members"
}
-// message GroupRequest{
-// string UserID = 1;
-// string GroupID = 2;
-// string HandleResult = 3;
-// string ReqMsg = 4;
-// string HandleMsg = 5;
-// int64 ReqTime = 6;
-// string HandleUserID = 7;
-// int64 HandleTime = 8;
-// string Ex = 9;
-// }open_im_sdk.GroupRequest == imdb.GroupRequest
type LocalGroupRequest struct {
GroupID string `gorm:"column:group_id;primary_key;type:varchar(64)" json:"groupID"`
GroupName string `gorm:"column:group_name;size:255" json:"groupName"`
@@ -235,17 +135,6 @@ type LocalGroupRequest struct {
InviterUserID string `gorm:"column:inviter_user_id;size:64" json:"inviterUserID"`
}
-// string UserID = 1;
-// string Nickname = 2;
-// string FaceUrl = 3;
-// int32 Gender = 4;
-// string PhoneNumber = 5;
-// string Birth = 6;
-// string Email = 7;
-// string Ex = 8;
-// int64 CreateTime = 9;
-// int32 AppMangerLevel = 10;
-// open_im_sdk.User == imdb.User
type LocalUser struct {
UserID string `gorm:"column:user_id;primary_key;type:varchar(64)" json:"userID"`
Nickname string `gorm:"column:name;type:varchar(255)" json:"nickname"`
@@ -257,15 +146,6 @@ type LocalUser struct {
GlobalRecvMsgOpt int32 `gorm:"column:global_recv_msg_opt" json:"globalRecvMsgOpt"`
}
-// message BlackInfo{
-// string OwnerUserID = 1;
-// int64 CreateTime = 2;
-// PublicUserInfo BlackUserInfo = 4;
-// int32 AddSource = 5;
-// string OperatorUserID = 6;
-// string Ex = 7;
-// }
-// open_im_sdk.BlackInfo(BlackUserInfo) != imdb.Black (BlockUserID)
type LocalBlack struct {
OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:varchar(64)" json:"ownerUserID"`
BlockUserID string `gorm:"column:block_user_id;primary_key;type:varchar(64)" json:"userID"`
@@ -289,125 +169,28 @@ type LocalSeq struct {
MinSeq uint32 `gorm:"column:min_seq"`
}
-// `create table if not exists chat_log (
-//
-// client_msg_id char(64) NOT NULL,
-// server_msg_id char(64) DEFAULT NULL,
-// send_id char(64) NOT NULL ,
-// is_read int NOT NULL ,
-// seq INTEGER DEFAULT NULL ,
-// status int NOT NULL ,
-// session_type int NOT NULL ,
-// recv_id char(64) NOT NULL ,
-// content_type int NOT NULL ,
-// sender_face_url varchar(100) DEFAULT NULL,
-// sender_nick_name varchar(64) DEFAULT NULL,
-// msg_from int NOT NULL ,
-// content varchar(1000) NOT NULL ,
-// sender_platform_id int NOT NULL ,
-// send_time INTEGER DEFAULT NULL ,
-// create_time INTEGER DEFAULT NULL,
-// ex varchar(1024) DEFAULT NULL,
-// PRIMARY KEY (client_msg_id)
-// )`
-
-// 删除会话,可能会话没有
-// 确认删除,告诉会话 ID
-// 清空聊天记录的发,会话有,但是聊天记录没有
-// DeleteMlessageFromlocalAndSvr
-// db
-
-// 不同的会话本地有一个单独的表,其中单聊的话也是这样,有一个单聊的表
-
-// 删除的话,先删除表,在删除本地的 seq ,最后清楚这个表。
-// 删除所有的消息的话,全部都是服务器来做,调用接口,然后客户端收到回调,然后删除本地的所有的信息。
-// 删除一条信息,删除最新的话,会话上有一条最新的消息,删除这条消息,会话上就没有消息了,此时显示的是第二条。
-// 和微信一样,我们 Go get error 分支,然后调用最新的 APi
-
type LocalChatLog struct {
- ClientMsgID string `gorm:"column:client_msg_id;primary_key;type:char(64)" json:"clientMsgID"`
- ServerMsgID string `gorm:"column:server_msg_id;type:char(64)" json:"serverMsgID"`
- SendID string `gorm:"column:send_id;type:char(64)" json:"sendID"`
- RecvID string `gorm:"column:recv_id;index:index_recv_id;type:char(64)" json:"recvID"`
- SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"`
- SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"`
- SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255)" json:"senderFaceURL"`
- SessionType int32 `gorm:"column:session_type" json:"sessionType"`
- MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"`
- ContentType int32 `gorm:"column:content_type;index:content_type_alone" json:"contentType"`
- Content string `gorm:"column:content;type:varchar(1000)" json:"content"`
- IsRead bool `gorm:"column:is_read" json:"isRead"`
- Status int32 `gorm:"column:status" json:"status"`
- Seq int64 `gorm:"column:seq;index:index_seq;default:0" json:"seq"`
- SendTime int64 `gorm:"column:send_time;index:index_send_time;" json:"sendTime"`
- CreateTime int64 `gorm:"column:create_time" json:"createTime"`
- AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
- Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
- LocalEx string `gorm:"column:local_ex;type:varchar(1024)" json:"localEx"`
- IsReact bool `gorm:"column:is_react" json:"isReact"`
- IsExternalExtensions bool `gorm:"column:is_external_extensions" json:"isExternalExtensions"`
- MsgFirstModifyTime int64 `gorm:"column:msg_first_modify_time" json:"msgFirstModifyTime"`
-}
-
-type LocalErrChatLog struct {
- Seq int64 `gorm:"column:seq;primary_key" json:"seq"`
- ClientMsgID string `gorm:"column:client_msg_id;type:char(64)" json:"clientMsgID"`
- ServerMsgID string `gorm:"column:server_msg_id;type:char(64)" json:"serverMsgID"`
- SendID string `gorm:"column:send_id;type:char(64)" json:"sendID"`
- RecvID string `gorm:"column:recv_id;type:char(64)" json:"recvID"`
- SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"`
- SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"`
- SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255)" json:"senderFaceURL"`
- SessionType int32 `gorm:"column:session_type" json:"sessionType"`
- MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"`
- ContentType int32 `gorm:"column:content_type" json:"contentType"`
- Content string `gorm:"column:content;type:varchar(1000)" json:"content"`
- IsRead bool `gorm:"column:is_read" json:"isRead"`
- Status int32 `gorm:"column:status" json:"status"`
- SendTime int64 `gorm:"column:send_time" json:"sendTime"`
- CreateTime int64 `gorm:"column:create_time" json:"createTime"`
- AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
- Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
-}
-type TempCacheLocalChatLog struct {
ClientMsgID string `gorm:"column:client_msg_id;primary_key;type:char(64)" json:"clientMsgID"`
ServerMsgID string `gorm:"column:server_msg_id;type:char(64)" json:"serverMsgID"`
SendID string `gorm:"column:send_id;type:char(64)" json:"sendID"`
- RecvID string `gorm:"column:recv_id;type:char(64)" json:"recvID"`
+ RecvID string `gorm:"column:recv_id;index:index_recv_id;type:char(64)" json:"recvID"`
SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"`
SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"`
SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255)" json:"senderFaceURL"`
SessionType int32 `gorm:"column:session_type" json:"sessionType"`
MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"`
- ContentType int32 `gorm:"column:content_type" json:"contentType"`
+ ContentType int32 `gorm:"column:content_type;index:content_type_alone" json:"contentType"`
Content string `gorm:"column:content;type:varchar(1000)" json:"content"`
IsRead bool `gorm:"column:is_read" json:"isRead"`
Status int32 `gorm:"column:status" json:"status"`
- Seq int64 `gorm:"column:seq;default:0" json:"seq"`
- SendTime int64 `gorm:"column:send_time;" json:"sendTime"`
+ Seq int64 `gorm:"column:seq;index:index_seq;default:0" json:"seq"`
+ SendTime int64 `gorm:"column:send_time;index:index_send_time;" json:"sendTime"`
CreateTime int64 `gorm:"column:create_time" json:"createTime"`
AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
+ LocalEx string `gorm:"column:local_ex;type:varchar(1024)" json:"localEx"`
}
-// `create table if not exists conversation (
-//
-// conversation_id char(128) NOT NULL,
-// conversation_type int(11) NOT NULL,
-// user_id varchar(128) DEFAULT NULL,
-// group_id varchar(128) DEFAULT NULL,
-// show_name varchar(128) NOT NULL,
-// face_url varchar(128) NOT NULL,
-// recv_msg_opt int(11) NOT NULL ,
-// unread_count int(11) NOT NULL ,
-// latest_msg varchar(255) NOT NULL ,
-// latest_msg_send_time INTEGER(255) NOT NULL ,
-// draft_text varchar(255) DEFAULT NULL ,
-// draft_timestamp INTEGER(255) DEFAULT NULL ,
-// is_pinned int(10) NOT NULL ,
-// PRIMARY KEY (conversation_id)
-//
-// )
type LocalConversation struct {
ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"`
ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"`
@@ -431,7 +214,6 @@ type LocalConversation struct {
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"`
MinSeq int64 `gorm:"column:min_seq" json:"minSeq"`
- HasReadSeq int64 `gorm:"column:has_read_seq" json:"hasReadSeq"`
MsgDestructTime int64 `gorm:"column:msg_destruct_time;default:604800" json:"msgDestructTime"`
IsMsgDestruct bool `gorm:"column:is_msg_destruct;default:false" json:"isMsgDestruct"`
}
@@ -447,17 +229,6 @@ type LocalConversationUnreadMessage struct {
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
}
-// message GroupRequest{
-// string UserID = 1;
-// string GroupID = 2;
-// string HandleResult = 3;
-// string ReqMsg = 4;
-// string HandleMsg = 5;
-// int64 ReqTime = 6;
-// string HandleUserID = 7;
-// int64 HandleTime = 8;
-// string Ex = 9;
-// }open_im_sdk.GroupRequest == imdb.GroupRequest
type LocalAdminGroupRequest struct {
LocalGroupRequest
}
@@ -466,36 +237,6 @@ type LocalChatLogReactionExtensions struct {
ClientMsgID string `gorm:"column:client_msg_id;primary_key;type:char(64)" json:"clientMsgID"`
LocalReactionExtensions []byte `gorm:"column:local_reaction_extensions" json:"localReactionExtensions"`
}
-type LocalWorkMomentsNotification struct {
- JsonDetail string `gorm:"column:json_detail"`
- CreateTime int64 `gorm:"create_time"`
-}
-
-type WorkMomentNotificationMsg struct {
- NotificationMsgType int32 `json:"notificationMsgType"`
- ReplyUserName string `json:"replyUserName"`
- ReplyUserID string `json:"replyUserID"`
- Content string `json:"content"`
- ContentID string `json:"contentID"`
- WorkMomentID string `json:"workMomentID"`
- UserID string `json:"userID"`
- UserName string `json:"userName"`
- FaceURL string `json:"faceURL"`
- WorkMomentContent string `json:"workMomentContent"`
- CreateTime int32 `json:"createTime"`
-}
-
-func (LocalWorkMomentsNotification) TableName() string {
- return "local_work_moments_notification"
-}
-
-type LocalWorkMomentsNotificationUnreadCount struct {
- UnreadCount int `gorm:"unread_count" json:"unreadCount"`
-}
-
-func (LocalWorkMomentsNotificationUnreadCount) TableName() string {
- return "local_work_moments_notification_unread_count"
-}
type NotificationSeqs struct {
ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"`
@@ -584,7 +325,8 @@ func (LocalVersionSync) TableName() string {
}
type LocalAppSDKVersion struct {
- Version string `gorm:"column:version;type:varchar(255);primary_key" json:"version"`
+ Version string `gorm:"column:version;type:varchar(255);primary_key" json:"version"`
+ Installed bool `gorm:"column:installed" json:"installed"` // Mark whether it has already been loaded
}
func (LocalAppSDKVersion) TableName() string {
diff --git a/pkg/db/notification_model.go b/pkg/db/notification_model.go
index a940338c9..e84f42bf8 100644
--- a/pkg/db/notification_model.go
+++ b/pkg/db/notification_model.go
@@ -44,8 +44,8 @@ func (d *DataBase) BatchInsertNotificationSeq(ctx context.Context, notificationS
}
func (d *DataBase) GetNotificationAllSeqs(ctx context.Context) ([]*model_struct.NotificationSeqs, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var seqs []*model_struct.NotificationSeqs
return seqs, errs.WrapMsg(d.conn.WithContext(ctx).Where("1=1").Find(&seqs).Error, "GetNotificationAllSeqs failed")
}
diff --git a/testv2/notification_db_test.go b/pkg/db/notification_model_test.go
similarity index 82%
rename from testv2/notification_db_test.go
rename to pkg/db/notification_model_test.go
index 9ec3ba205..8a28cb0c3 100644
--- a/testv2/notification_db_test.go
+++ b/pkg/db/notification_model_test.go
@@ -1,16 +1,15 @@
-package testv2
+package db
import (
"context"
"testing"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
)
func Test_BatchInsertNotificationSeq(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
diff --git a/pkg/db/sending_messages_model.go b/pkg/db/sending_messages_model.go
index 6947faded..a94a2c159 100644
--- a/pkg/db/sending_messages_model.go
+++ b/pkg/db/sending_messages_model.go
@@ -31,13 +31,13 @@ func (d *DataBase) InsertSendingMessage(ctx context.Context, message *model_stru
}
func (d *DataBase) DeleteSendingMessage(ctx context.Context, conversationID, clientMsgID string) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
localSendingMessage := model_struct.LocalSendingMessages{ConversationID: conversationID, ClientMsgID: clientMsgID}
return errs.WrapMsg(d.conn.WithContext(ctx).Delete(&localSendingMessage).Error, "DeleteSendingMessage failed")
}
func (d *DataBase) GetAllSendingMessages(ctx context.Context) (friendRequests []*model_struct.LocalSendingMessages, err error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
return friendRequests, errs.WrapMsg(d.conn.WithContext(ctx).Find(&friendRequests).Error, "GetAllSendingMessages failed")
}
diff --git a/pkg/db/seq_data_model.go b/pkg/db/seq_data_model.go
index 5c98468e4..7c3432d62 100644
--- a/pkg/db/seq_data_model.go
+++ b/pkg/db/seq_data_model.go
@@ -25,11 +25,15 @@ import (
)
func (d *DataBase) GetMinSeq(ctx context.Context, ID string) (uint32, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var seqData model_struct.LocalSeq
return seqData.MinSeq, errs.WrapMsg(d.conn.WithContext(ctx).First(&seqData).Error, "GetMinSeq failed")
}
func (d *DataBase) SetMinSeq(ctx context.Context, ID string, minSeq uint32) error {
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
seqData := model_struct.LocalSeq{ID: ID, MinSeq: minSeq}
t := d.conn.WithContext(ctx).Updates(&seqData)
if t.RowsAffected == 0 {
diff --git a/pkg/db/stranger_model.go b/pkg/db/stranger_model.go
deleted file mode 100644
index af7242844..000000000
--- a/pkg/db/stranger_model.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
- "errors"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/tools/errs"
- "gorm.io/gorm"
-)
-
-func (d *DataBase) GetStrangerInfo(ctx context.Context, userIDs []string) ([]*model_struct.LocalStranger, error) {
- d.friendMtx.Lock()
- defer d.friendMtx.Unlock()
- var friendList []*model_struct.LocalStranger
- return friendList, errs.WrapMsg(d.conn.WithContext(ctx).Where("user_id IN ?", userIDs).Find(&friendList).Error, "GetFriendInfoListByFriendUserID failed")
-}
-
-func (d *DataBase) SetStrangerInfo(ctx context.Context, localStrangerList []*model_struct.LocalStranger) error {
- //TODO Can be optimized into two chan batch update or insert operations
- for _, existingData := range localStrangerList {
- result := d.conn.First(&existingData, "user_id = ?", existingData.UserID)
- if errors.Is(result.Error, gorm.ErrRecordNotFound) {
- // Data does not exist, perform insert operation
- err := d.conn.Create(&existingData).Error
- return err
- } else if result.Error != nil {
- return result.Error
- }
- err := d.conn.Save(&existingData).Error
- if err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/pkg/db/super_group_chat_log_model.go b/pkg/db/super_group_chat_log_model.go
deleted file mode 100644
index b7c81f672..000000000
--- a/pkg/db/super_group_chat_log_model.go
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/errs"
-
- "gorm.io/gorm"
-)
-
-func (d *DataBase) initSuperLocalChatLog(ctx context.Context, groupID string) {
- if !d.conn.WithContext(ctx).Migrator().HasTable(utils.GetConversationTableName(groupID)) {
- d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).AutoMigrate(&model_struct.LocalChatLog{})
- }
-}
-func (d *DataBase) SuperGroupBatchInsertMessageList(ctx context.Context, MessageList []*model_struct.LocalChatLog, groupID string) error {
- if MessageList == nil {
- return nil
- }
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Create(MessageList).Error, "SuperGroupBatchInsertMessageList failed")
-}
-func (d *DataBase) SuperGroupInsertMessage(ctx context.Context, Message *model_struct.LocalChatLog, groupID string) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Create(Message).Error, "SuperGroupInsertMessage failed")
-}
-func (d *DataBase) SuperGroupDeleteAllMessage(ctx context.Context, groupID string) error {
- return errs.WrapMsg(d.conn.WithContext(ctx).Session(&gorm.Session{AllowGlobalUpdate: true}).Table(utils.GetConversationTableName(groupID)).Delete(&model_struct.LocalChatLog{}).Error, "SuperGroupDeleteAllMessage failed")
-}
-func (d *DataBase) SuperGroupSearchMessageByKeyword(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, sourceID string, startTime, endTime int64, sessionType, offset, count int) (result []*model_struct.LocalChatLog, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var messageList []*model_struct.LocalChatLog
- var condition string
- var subCondition string
- if keywordListMatchType == constant.KeywordMatchOr {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "or "
-
- }
- }
- } else {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "and "
- }
- }
- }
- condition = fmt.Sprintf("recv_id=%q And send_time between %d and %d AND status <=%d And content_type IN ? ", sourceID, startTime, endTime, constant.MsgStatusSendFailed)
- condition += subCondition
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(sourceID)).Where(condition, contentType).Order("send_time DESC").Offset(offset).Limit(count).Find(&messageList).Error, "InsertMessage failed")
-}
-
-func (d *DataBase) SuperGroupSearchAllMessageByContentType(ctx context.Context, groupID string, contentType int32) (result []*model_struct.LocalChatLog, err error) {
- err = d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where("content_type = ?", contentType).Find(&result).Error
- return result, err
-}
-
-func (d *DataBase) SuperGroupSearchMessageByContentType(ctx context.Context, contentType []int, sourceID string, startTime, endTime int64, sessionType, offset, count int) (result []*model_struct.LocalChatLog, err error) {
- var messageList []*model_struct.LocalChatLog
- condition := fmt.Sprintf("session_type=%d And recv_id==%q And send_time between %d and %d AND status <=%d And content_type IN ?", sessionType, sourceID, startTime, endTime, constant.MsgStatusSendFailed)
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(sourceID)).Where(condition, contentType).Order("send_time DESC").Offset(offset).Limit(count).Find(&messageList).Error, "SearchMessage failed")
-}
-
-func (d *DataBase) SuperGroupSearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, startTime, endTime int64, groupID string) (result []*model_struct.LocalChatLog, err error) {
- var messageList []*model_struct.LocalChatLog
- var condition string
- var subCondition string
- if keywordListMatchType == constant.KeywordMatchOr {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "or "
-
- }
- }
- } else {
- for i := 0; i < len(keywordList); i++ {
- if i == 0 {
- subCondition += "And ("
- }
- if i+1 >= len(keywordList) {
- subCondition += "content like " + "'%" + keywordList[i] + "%') "
- } else {
- subCondition += "content like " + "'%" + keywordList[i] + "%' " + "and "
- }
- }
- }
- condition = fmt.Sprintf("send_time between %d and %d AND status <=%d And content_type IN ? ", startTime, endTime, constant.MsgStatusSendFailed)
- condition += subCondition
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where(condition, contentType).Order("send_time DESC").Find(&messageList).Error, "SearchMessage failed")
-}
-func (d *DataBase) SuperGroupBatchUpdateMessageList(ctx context.Context, MessageList []*model_struct.LocalChatLog) error {
- if MessageList == nil {
- return nil
- }
-
- for _, v := range MessageList {
- v1 := new(model_struct.LocalChatLog)
- v1.ClientMsgID = v.ClientMsgID
- v1.Seq = v.Seq
- v1.Status = v.Status
- err := d.SuperGroupUpdateMessage(ctx, v1)
- if err != nil {
- return errs.WrapMsg(err, "BatchUpdateMessageList failed")
- }
-
- }
- return nil
-}
-
-func (d *DataBase) SuperGroupMessageIfExists(ctx context.Context, ClientMsgID string) (bool, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var count int64
- t := d.conn.WithContext(ctx).Model(&model_struct.LocalChatLog{}).Where("client_msg_id = ?",
- ClientMsgID).Count(&count)
- if t.Error != nil {
- return false, errs.WrapMsg(t.Error, "MessageIfExists get failed")
- }
- if count != 1 {
- return false, nil
- } else {
- return true, nil
- }
-}
-func (d *DataBase) SuperGroupIsExistsInErrChatLogBySeq(ctx context.Context, seq int64) bool {
- return true
-}
-func (d *DataBase) SuperGroupMessageIfExistsBySeq(ctx context.Context, seq int64) (bool, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var count int64
- t := d.conn.WithContext(ctx).Model(&model_struct.LocalChatLog{}).Where("seq = ?",
- seq).Count(&count)
- if t.Error != nil {
- return false, errs.WrapMsg(t.Error, "MessageIfExistsBySeq get failed")
- }
- if count != 1 {
- return false, nil
- } else {
- return true, nil
- }
-}
-func (d *DataBase) SuperGroupGetMessage(ctx context.Context, msg *sdk_struct.MsgStruct) (*model_struct.LocalChatLog, error) {
- d.initSuperLocalChatLog(ctx, msg.GroupID)
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var c model_struct.LocalChatLog
- return &c, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(msg.GroupID)).Where("client_msg_id = ?",
- msg.ClientMsgID).Take(&c).Error, "GetMessage failed")
-}
-
-func (d *DataBase) SuperGroupGetAllUnDeleteMessageSeqList(ctx context.Context) ([]uint32, error) {
- var seqList []uint32
- return seqList, errs.WrapMsg(d.conn.WithContext(ctx).Model(&model_struct.LocalChatLog{}).Where("status != 4").Select("seq").Find(&seqList).Error, "")
-}
-
-func (d *DataBase) SuperGroupUpdateColumnsMessage(ctx context.Context, ClientMsgID, groupID string, args map[string]interface{}) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where(
- "client_msg_id = ? ", ClientMsgID).Updates(args)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed")
-}
-func (d *DataBase) SuperGroupUpdateMessage(ctx context.Context, c *model_struct.LocalChatLog) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(c.RecvID)).Updates(c)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateMessage failed")
-}
-func (d *DataBase) SuperGroupUpdateSpecificContentTypeMessage(ctx context.Context, contentType int, groupID string, args map[string]interface{}) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where("content_type = ?", contentType).Updates(args)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateMessage failed")
-}
-
-//funcation (d *DataBase) SuperGroupDeleteAllMessage(ctx context.Context, ) error {
-// d.mRWMutex.Lock()
-// defer d.mRWMutex.Unlock()
-// err := d.conn.WithContext(ctx).Model(&model_struct.LocalChatLog{}).Exec("update local_chat_logs set status = ?,content = ? ", constant.MsgStatusHasDeleted, "").Error
-// return errs.WrapMsg(err, "delete all message error")
-//}
-
-func (d *DataBase) SuperGroupUpdateMessageStatusBySourceID(ctx context.Context, sourceID string, status, sessionType int32) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var condition string
- if sourceID == d.loginUserID && sessionType == constant.SingleChatType {
- condition = "send_id=? And recv_id=? AND session_type=?"
- } else {
- condition = "(send_id=? or recv_id=?)AND session_type=?"
- }
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(sourceID)).Where(condition, sourceID, sourceID, sessionType).Updates(model_struct.LocalChatLog{Status: status})
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateMessageStatusBySourceID failed")
-}
-func (d *DataBase) SuperGroupUpdateMessageTimeAndStatus(ctx context.Context, msg *sdk_struct.MsgStruct) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(msg.GroupID)).Where("client_msg_id=? And seq=?", msg.ClientMsgID, 0).Updates(model_struct.LocalChatLog{Status: msg.Status, SendTime: msg.SendTime, ServerMsgID: msg.ServerMsgID})
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "SuperGroupUpdateMessageTimeAndStatus failed")
-}
-
-func (d *DataBase) SuperGroupGetMessageList(ctx context.Context, sourceID string, sessionType, count int, startTime int64, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var messageList []*model_struct.LocalChatLog
- var condition, timeOrder, timeSymbol string
- if isReverse {
- timeOrder = "send_time ASC"
- timeSymbol = ">"
- } else {
- timeOrder = "send_time DESC"
- timeSymbol = "<"
- }
- condition = " recv_id = ? AND status <=? And session_type = ? And send_time " + timeSymbol + " ?"
-
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(sourceID)).Where(condition, sourceID, constant.MsgStatusSendFailed, sessionType, startTime).
- Order(timeOrder).Offset(0).Limit(count).Find(&messageList).Error, "GetMessageList failed")
-}
-func (d *DataBase) SuperGroupGetMessageListNoTime(ctx context.Context, sourceID string, sessionType, count int, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- d.initSuperLocalChatLog(ctx, sourceID)
- var messageList []*model_struct.LocalChatLog
- var condition, timeOrder string
- if isReverse {
- timeOrder = "send_time ASC"
- } else {
- timeOrder = "send_time DESC"
- }
- condition = "recv_id = ? AND status <=? And session_type = ? "
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(sourceID)).Where(condition, sourceID, constant.MsgStatusSendFailed, sessionType).
- Order(timeOrder).Offset(0).Limit(count).Find(&messageList).Error, "GetMessageList failed")
-}
-
-func (d *DataBase) SuperGroupGetSendingMessageList(ctx context.Context, groupID string) (result []*model_struct.LocalChatLog, err error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var messageList []*model_struct.LocalChatLog
- return messageList, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where("status = ?", constant.MsgStatusSending).Find(&messageList).Error, "GetMessageList failed")
-}
-
-func (d *DataBase) SuperGroupUpdateGroupMessageHasRead(ctx context.Context, msgIDList []string, groupID string) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where(" client_msg_id in ?", msgIDList).Update("is_read", constant.HasRead)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateMessageStatusBySourceID failed")
-}
-func (d *DataBase) SuperGroupUpdateGroupMessageFields(ctx context.Context, msgIDList []string, groupID string, args map[string]interface{}) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- t := d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where(" client_msg_id in ?", msgIDList).Updates(args)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "UpdateMessageStatusBySourceID failed")
-}
-
-func (d *DataBase) SuperGroupGetNormalMsgSeq(ctx context.Context) (int64, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seq int64
- return seq, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Select("IFNULL(max(seq),0)").Find(&seq).Error, "GetNormalMsgSeq")
-}
-func (d *DataBase) SuperGroupGetNormalMinSeq(ctx context.Context, groupID string) (int64, error) {
- var seq int64
- return seq, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Select("IFNULL(min(seq),0)").Where("seq >?", 0).Find(&seq).Error, "SuperGroupGetNormalMinSeq")
-}
-func (d *DataBase) SuperGroupGetTestMessage(ctx context.Context, seq int64) (*model_struct.LocalChatLog, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var c model_struct.LocalChatLog
- return &c, errs.WrapMsg(d.conn.WithContext(ctx).Where("seq = ?",
- seq).Find(&c).Error, "GetTestMessage failed")
-}
-
-func (d *DataBase) SuperGroupUpdateMsgSenderNickname(ctx context.Context, sendID, nickname string, sType int) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Where(
- "send_id = ? and session_type = ? and sender_nick_name != ? ", sendID, sType, nickname).Updates(
- map[string]interface{}{"sender_nick_name": nickname}).Error, utils.GetSelfFuncName()+" failed")
-}
-
-func (d *DataBase) SuperGroupUpdateMsgSenderFaceURL(ctx context.Context, sendID, faceURL string, sType int) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Where(
- "send_id = ? and session_type = ? and sender_face_url != ? ", sendID, sType, faceURL).Updates(
- map[string]interface{}{"sender_face_url": faceURL}).Error, utils.GetSelfFuncName()+" failed")
-}
-func (d *DataBase) SuperGroupUpdateMsgSenderFaceURLAndSenderNickname(ctx context.Context, sendID, faceURL, nickname string, sessionType int, groupID string) error {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Where(
- "send_id = ? and session_type = ?", sendID, sessionType).Updates(
- map[string]interface{}{"sender_face_url": faceURL, "sender_nick_name": nickname}).Error, utils.GetSelfFuncName()+" failed")
-}
-
-func (d *DataBase) SuperGroupGetMsgSeqByClientMsgID(ctx context.Context, clientMsgID string, groupID string) (uint32, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seq uint32
- return seq, errs.WrapMsg(d.conn.WithContext(ctx).Table(utils.GetConversationTableName(groupID)).Select("seq").Where("client_msg_id=?", clientMsgID).Take(&seq).Error, utils.GetSelfFuncName()+" failed")
-}
-
-func (d *DataBase) SuperGroupGetMsgSeqListByGroupID(ctx context.Context, groupID string) ([]uint32, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seqList []uint32
- return seqList, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Select("seq").Where("recv_id=?", groupID).Find(&seqList).Error, utils.GetSelfFuncName()+" failed")
-}
-
-func (d *DataBase) SuperGroupGetMsgSeqListByPeerUserID(ctx context.Context, userID string) ([]uint32, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seqList []uint32
- return seqList, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Select("seq").Where("recv_id=? or send_id=?", userID, userID).Find(&seqList).Error, utils.GetSelfFuncName()+" failed")
-}
-
-func (d *DataBase) SuperGroupGetMsgSeqListBySelfUserID(ctx context.Context, userID string) ([]uint32, error) {
- d.mRWMutex.Lock()
- defer d.mRWMutex.Unlock()
- var seqList []uint32
- return seqList, errs.WrapMsg(d.conn.WithContext(ctx).Model(model_struct.LocalChatLog{}).Select("seq").Where("recv_id=? and send_id=?", userID, userID).Find(&seqList).Error, utils.GetSelfFuncName()+" failed")
-}
diff --git a/pkg/db/super_group_model.go b/pkg/db/super_group_model.go
deleted file mode 100644
index 6b9813831..000000000
--- a/pkg/db/super_group_model.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
- "errors"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/tools/errs"
-)
-
-func (d *DataBase) InsertSuperGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error {
- d.superGroupMtx.Lock()
- defer d.superGroupMtx.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(constant.SuperGroupTableName).Create(groupInfo).Error, "InsertSuperGroup failed")
-}
-
-func (d *DataBase) DeleteAllSuperGroup(ctx context.Context) error {
- d.superGroupMtx.Lock()
- defer d.superGroupMtx.Unlock()
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(constant.SuperGroupTableName).Delete(&model_struct.LocalGroup{}).Error, "DeleteAllSuperGroup failed")
-}
-
-func (d *DataBase) GetSuperGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
- d.superGroupMtx.Lock()
- defer d.superGroupMtx.Unlock()
- var g model_struct.LocalGroup
- return &g, errs.WrapMsg(d.conn.WithContext(ctx).Table(constant.SuperGroupTableName).Where("group_id = ?", groupID).Take(&g).Error, "GetGroupList failed")
-}
-
-func (d *DataBase) UpdateSuperGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error {
- d.superGroupMtx.Lock()
- defer d.superGroupMtx.Unlock()
-
- t := d.conn.WithContext(ctx).Table(constant.SuperGroupTableName).Select("*").Updates(*groupInfo)
- if t.RowsAffected == 0 {
- return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
- }
- return errs.WrapMsg(t.Error, "")
-}
-
-func (d *DataBase) DeleteSuperGroup(ctx context.Context, groupID string) error {
- d.superGroupMtx.Lock()
- defer d.superGroupMtx.Unlock()
- localGroup := model_struct.LocalGroup{GroupID: groupID}
- return errs.WrapMsg(d.conn.WithContext(ctx).Table(constant.SuperGroupTableName).Delete(&localGroup).Error, "DeleteSuperGroup failed")
-}
diff --git a/pkg/db/table_master.go b/pkg/db/table_master.go
index 1b20c359f..a4c739fa6 100644
--- a/pkg/db/table_master.go
+++ b/pkg/db/table_master.go
@@ -10,6 +10,8 @@ import (
)
func (d *DataBase) GetExistTables(ctx context.Context) ([]string, error) {
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var tables []string
return tables, errs.Wrap(d.conn.WithContext(ctx).Raw("SELECT name FROM sqlite_master WHERE type='table'").Scan(&tables).Error)
diff --git a/pkg/db/temp_cache_chat_log_model.go b/pkg/db/temp_cache_chat_log_model.go
deleted file mode 100644
index a6f6590e6..000000000
--- a/pkg/db/temp_cache_chat_log_model.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/tools/errs"
-)
-
-func (d *DataBase) BatchInsertTempCacheMessageList(ctx context.Context, MessageList []*model_struct.TempCacheLocalChatLog) error {
- if MessageList == nil {
- return nil
- }
- return errs.WrapMsg(d.conn.WithContext(ctx).Create(MessageList).Error, "BatchInsertTempCacheMessageList failed")
-}
-func (d *DataBase) InsertTempCacheMessage(ctx context.Context, Message *model_struct.TempCacheLocalChatLog) error {
-
- return errs.WrapMsg(d.conn.WithContext(ctx).Create(Message).Error, "InsertTempCacheMessage failed")
-
-}
diff --git a/pkg/db/upload_model.go b/pkg/db/upload_model.go
index 5a3a5fc44..f9374325e 100644
--- a/pkg/db/upload_model.go
+++ b/pkg/db/upload_model.go
@@ -48,8 +48,8 @@ func (d *DataBase) deleteUpload(ctx context.Context, partHash string) error {
}
func (d *DataBase) UpdateUpload(ctx context.Context, upload *model_struct.LocalUpload) error {
- d.groupMtx.Lock()
- defer d.groupMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.Wrap(d.conn.WithContext(ctx).Updates(upload).Error)
}
diff --git a/pkg/db/user_command.go b/pkg/db/user_command.go
index 2153a9151..af18d1a99 100644
--- a/pkg/db/user_command.go
+++ b/pkg/db/user_command.go
@@ -27,8 +27,8 @@ import (
// ProcessUserCommandAdd adds a new user command to the database.
func (d *DataBase) ProcessUserCommandAdd(ctx context.Context, command *model_struct.LocalUserCommand) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
userCommand := model_struct.LocalUserCommand{
UserID: command.UserID,
@@ -44,8 +44,8 @@ func (d *DataBase) ProcessUserCommandAdd(ctx context.Context, command *model_str
// ProcessUserCommandUpdate updates an existing user command in the database.
func (d *DataBase) ProcessUserCommandUpdate(ctx context.Context, command *model_struct.LocalUserCommand) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(command).Select("*").Updates(*command)
if t.RowsAffected == 0 {
@@ -57,16 +57,16 @@ func (d *DataBase) ProcessUserCommandUpdate(ctx context.Context, command *model_
// ProcessUserCommandDelete deletes a user command from the database.
func (d *DataBase) ProcessUserCommandDelete(ctx context.Context, command *model_struct.LocalUserCommand) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Where("type = ? AND uuid = ?", command.Type, command.Uuid).Delete(&model_struct.LocalUserCommand{}).Error,
"ProcessUserCommandDelete failed")
}
// ProcessUserCommandGetAll retrieves user commands from the database.
func (d *DataBase) ProcessUserCommandGetAll(ctx context.Context) ([]*model_struct.LocalUserCommand, error) {
- d.userMtx.RLock()
- defer d.userMtx.RUnlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var commands []*model_struct.LocalUserCommand
return commands, errs.WrapMsg(d.conn.WithContext(ctx).Find(&commands).Error, "ProcessUserCommandGetAll failed")
}
diff --git a/pkg/db/user_model.go b/pkg/db/user_model.go
index 6500fd9c5..2365117e6 100644
--- a/pkg/db/user_model.go
+++ b/pkg/db/user_model.go
@@ -21,17 +21,19 @@ import (
"context"
"errors"
+ "gorm.io/gorm"
+
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/tools/errs"
)
func (d *DataBase) GetLoginUser(ctx context.Context, userID string) (*model_struct.LocalUser, error) {
- d.userMtx.RLock()
- defer d.userMtx.RUnlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var user model_struct.LocalUser
err := d.conn.WithContext(ctx).Where("user_id = ? ", userID).Take(&user).Error
if err != nil {
- if err == errs.ErrRecordNotFound {
+ if err == gorm.ErrRecordNotFound {
return nil, errs.ErrRecordNotFound.Wrap()
}
return nil, errs.Wrap(err)
@@ -40,8 +42,8 @@ func (d *DataBase) GetLoginUser(ctx context.Context, userID string) (*model_stru
}
func (d *DataBase) UpdateLoginUser(ctx context.Context, user *model_struct.LocalUser) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(user).Select("*").Updates(user)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -49,8 +51,8 @@ func (d *DataBase) UpdateLoginUser(ctx context.Context, user *model_struct.Local
return errs.WrapMsg(t.Error, "UpdateLoginUser failed")
}
func (d *DataBase) UpdateLoginUserByMap(ctx context.Context, user *model_struct.LocalUser, args map[string]interface{}) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
t := d.conn.WithContext(ctx).Model(&user).Updates(args)
if t.RowsAffected == 0 {
return errs.WrapMsg(errors.New("RowsAffected == 0"), "no update")
@@ -58,7 +60,7 @@ func (d *DataBase) UpdateLoginUserByMap(ctx context.Context, user *model_struct.
return errs.WrapMsg(t.Error, "UpdateColumnsConversation failed")
}
func (d *DataBase) InsertLoginUser(ctx context.Context, user *model_struct.LocalUser) error {
- d.userMtx.Lock()
- defer d.userMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
return errs.WrapMsg(d.conn.WithContext(ctx).Create(user).Error, "InsertLoginUser failed")
}
diff --git a/pkg/db/version_sync.go b/pkg/db/version_sync.go
index c010a8524..ce6c65bd1 100644
--- a/pkg/db/version_sync.go
+++ b/pkg/db/version_sync.go
@@ -19,6 +19,7 @@ package db
import (
"context"
+ "errors"
"gorm.io/gorm"
@@ -27,12 +28,12 @@ import (
)
func (d *DataBase) GetVersionSync(ctx context.Context, tableName, entityID string) (*model_struct.LocalVersionSync, error) {
- d.versionMtx.RLock()
- defer d.versionMtx.RUnlock()
+ d.mRWMutex.RLock()
+ defer d.mRWMutex.RUnlock()
var res model_struct.LocalVersionSync
err := d.conn.WithContext(ctx).Where("`table_name` = ? and `entity_id` = ?", tableName, entityID).Take(&res).Error
if err != nil {
- if err == gorm.ErrRecordNotFound {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
return &model_struct.LocalVersionSync{}, errs.ErrRecordNotFound.Wrap()
}
return nil, errs.Wrap(err)
@@ -41,8 +42,8 @@ func (d *DataBase) GetVersionSync(ctx context.Context, tableName, entityID strin
}
func (d *DataBase) SetVersionSync(ctx context.Context, lv *model_struct.LocalVersionSync) error {
- d.versionMtx.Lock()
- defer d.versionMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
var existing model_struct.LocalVersionSync
err := d.conn.WithContext(ctx).Where("`table_name` = ? AND `entity_id` = ?", lv.Table, lv.EntityID).First(&existing).Error
@@ -64,8 +65,8 @@ func (d *DataBase) SetVersionSync(ctx context.Context, lv *model_struct.LocalVer
}
func (d *DataBase) DeleteVersionSync(ctx context.Context, tableName, entityID string) error {
- d.versionMtx.Lock()
- defer d.versionMtx.Unlock()
+ d.mRWMutex.Lock()
+ defer d.mRWMutex.Unlock()
localVersionSync := model_struct.LocalVersionSync{Table: tableName, EntityID: entityID}
return errs.WrapMsg(d.conn.WithContext(ctx).Delete(&localVersionSync).Error, "DeleteVersionSync failed")
}
diff --git a/testv2/version_sync_db_test.go b/pkg/db/version_sync_test.go
similarity index 84%
rename from testv2/version_sync_db_test.go
rename to pkg/db/version_sync_test.go
index c4d29d6a7..b4ac54d40 100644
--- a/testv2/version_sync_db_test.go
+++ b/pkg/db/version_sync_test.go
@@ -1,17 +1,16 @@
-package testv2
+package db
import (
"context"
"testing"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
)
func Test_GetVersionSync(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -29,7 +28,7 @@ func Test_GetVersionSync(t *testing.T) {
func Test_SetVersionSync(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
@@ -56,7 +55,7 @@ func Test_SetVersionSync(t *testing.T) {
func Test_DeleteVersionSync(t *testing.T) {
ctx := context.Background()
- db, err := db.NewDataBase(ctx, "1695766238", "./", 6)
+ db, err := NewDataBase(ctx, "1695766238", "./", 6)
if err != nil {
return
}
diff --git a/pkg/db/working_group.go b/pkg/db/working_group.go
deleted file mode 100644
index 5776fdda0..000000000
--- a/pkg/db/working_group.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build !js
-// +build !js
-
-package db
-
-import (
- "context"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/tools/errs"
-)
-
-func (d *DataBase) GetJoinedWorkingGroupIDList(ctx context.Context) ([]string, error) {
- groupList, err := d.GetJoinedGroupListDB(ctx)
- if err != nil {
- return nil, errs.WrapMsg(err, "")
- }
- var groupIDList []string
- for _, v := range groupList {
- if v.GroupType == constant.WorkingGroup {
- groupIDList = append(groupIDList, v.GroupID)
- }
- }
- return groupIDList, nil
-}
-
-func (d *DataBase) GetJoinedWorkingGroupList(ctx context.Context) ([]*model_struct.LocalGroup, error) {
- groupList, err := d.GetJoinedGroupListDB(ctx)
- var transfer []*model_struct.LocalGroup
- for _, v := range groupList {
- if v.GroupType == constant.WorkingGroup {
- transfer = append(transfer, v)
- }
- }
- return transfer, errs.WrapMsg(err, "GetJoinedSuperGroupList failed ")
-}
diff --git a/pkg/network/http_client.go b/pkg/network/http_client.go
index dfbb42848..ae813b726 100644
--- a/pkg/network/http_client.go
+++ b/pkg/network/http_client.go
@@ -16,88 +16,285 @@ package network
import (
"bytes"
+ "compress/gzip"
+ "context"
"encoding/json"
- "errors"
- "net"
-
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "io/ioutil"
+ "fmt"
+ "io"
"net/http"
+ "reflect"
"time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/page"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
+ "github.com/openimsdk/tools/errs"
+
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/log"
)
-func get(url string) (response []byte, err error) {
- client := http.Client{Timeout: 5 * time.Second}
- resp, err := client.Get(url)
+// apiClient is a global HTTP client with a timeout of one minute.
+var apiClient = &http.Client{
+ Timeout: time.Second * 10,
+}
+
+// ApiResponse represents the standard structure of an API response.
+type ApiResponse struct {
+ ErrCode int `json:"errCode"`
+ ErrMsg string `json:"errMsg"`
+ ErrDlt string `json:"errDlt"`
+ Data json.RawMessage `json:"data"`
+}
+
+// ApiPost performs an HTTP POST request to a specified API endpoint.
+// It serializes the request object, sends it to the API, and unmarshals the response into the resp object.
+// It handles logging, error wrapping, and operation ID validation.
+// Context (ctx) is used for passing metadata and control information.
+// api: the API endpoint to which the request is sent.
+// req: the request object to be sent to the API.
+// resp: a pointer to the response object where the API response will be unmarshalled.
+// Returns an error if the request fails at any stage.
+func ApiPost(ctx context.Context, api string, req, resp any) (err error) {
+ // Extract operationID from context and validate.
+
+ //If ctx is empty, it may be because the ctx from the cmd's context is not passed in.
+ operationID, _ := ctx.Value("operationID").(string)
+ if operationID == "" {
+ err := sdkerrs.ErrArgs.WrapMsg("call api operationID is empty")
+ log.ZError(ctx, "ApiRequest", err, "type", "ctx not set operationID")
+ return err
+ }
+
+ // Deferred function to log the result of the API call.
+ defer func(start time.Time) {
+ elapsed := time.Since(start).Milliseconds()
+ if err == nil {
+ log.ZDebug(ctx, "CallApi", "duration", fmt.Sprintf("%dms", elapsed), "api", api, "state", "success")
+ } else {
+ log.ZError(ctx, "CallApi", err, "duration", fmt.Sprintf("%dms", elapsed), "api", api, "state", "failed")
+ }
+ }(time.Now())
+
+ // Serialize the request object to JSON.
+ reqBody, err := json.Marshal(req)
if err != nil {
- return nil, err
+ log.ZError(ctx, "ApiRequest", err, "type", "json.Marshal(req) failed")
+ return sdkerrs.ErrSdkInternal.WrapMsg("json.Marshal(req) failed " + err.Error())
}
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
+
+ // Construct the full API URL and create a new HTTP request with context.
+ ctxInfo := ccontext.Info(ctx)
+ reqUrl := ctxInfo.ApiAddr() + api
+ request, err := http.NewRequestWithContext(ctx, http.MethodPost, reqUrl, bytes.NewReader(reqBody))
if err != nil {
- return nil, err
+ log.ZError(ctx, "ApiRequest", err, "type", "http.NewRequestWithContext failed")
+ return sdkerrs.ErrSdkInternal.WrapMsg("sdk http.NewRequestWithContext failed " + err.Error())
}
- return body, nil
-}
-func retry(url string, data interface{}, token string, attempts int, sleep time.Duration, fn func(string, interface{}, string) ([]byte, error)) ([]byte, error) {
- b, err := fn(url, data, token)
+
+ // Set headers for the request.
+ log.ZDebug(ctx, "ApiRequest", "url", reqUrl, "token", ctxInfo.Token(), "body", string(reqBody))
+ request.ContentLength = int64(len(reqBody))
+ request.Header.Set("Content-Type", "application/json")
+ request.Header.Set("operationID", operationID)
+ request.Header.Set("token", ctxInfo.Token())
+ request.Header.Set("Accept-Encoding", "gzip")
+
+ // Send the request and receive the response.
+ response, err := apiClient.Do(request)
if err != nil {
- if attempts--; attempts > 0 {
- time.Sleep(sleep)
- return retry(url, data, token, attempts, 2*sleep, fn)
- }
+ log.ZError(ctx, "ApiRequest", err, "type", "network error")
+ return sdkerrs.ErrNetwork.WrapMsg("ApiPost http.Client.Do failed " + err.Error())
+ }
+
+ // Ensure the response body is closed after processing.
+ defer response.Body.Close()
+ var body io.ReadCloser
+ switch contentEncoding := response.Header.Get("Content-Encoding"); contentEncoding {
+ case "":
+ body = response.Body
+ case "gzip":
+ body, err = gzip.NewReader(response.Body)
+ defer body.Close()
+ default:
+ log.ZWarn(ctx, "http response content encoding not supported", nil, "url", reqUrl, "contentEncoding", contentEncoding)
+ body = response.Body
+ }
+ // Read the response body.
+ respBody, err := io.ReadAll(body)
+ if err != nil {
+ log.ZError(ctx, "ApiResponse", err, "type", "read body", "status", response.Status)
+ return sdkerrs.ErrSdkInternal.WrapMsg("io.ReadAll(ApiResponse) failed " + err.Error())
+ }
+
+ // Log the response for debugging purposes.
+ log.ZDebug(ctx, "ApiResponse", "url", reqUrl, "status", response.Status, "body", string(respBody))
+
+ // Unmarshal the response body into the ApiResponse structure.
+ var baseApi ApiResponse
+ if err := json.Unmarshal(respBody, &baseApi); err != nil {
+ log.ZError(ctx, "ApiResponse", err, "type", "api code parse")
+ return sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("api %s json.Unmarshal(%q, %T) failed %s", api, string(respBody), &baseApi, err.Error()))
+ }
+
+ // Check if the API returned an error code and handle it.
+ if baseApi.ErrCode != 0 {
+ err := sdkerrs.New(baseApi.ErrCode, baseApi.ErrMsg, baseApi.ErrDlt)
+ ccontext.GetApiErrCodeCallback(ctx).OnError(ctx, err)
+ log.ZError(ctx, "ApiResponse", err, "type", "api code error", "msg", baseApi.ErrMsg, "dlt", baseApi.ErrDlt)
+ return err
+ }
+
+ // If no data is received, or it's null, return with no error.
+ if resp == nil || len(baseApi.Data) == 0 || string(baseApi.Data) == "null" {
+ return nil
+ }
+
+ // Unmarshal the actual data part of the response into the provided response object.
+ if err := json.Unmarshal(baseApi.Data, resp); err != nil {
+ log.ZError(ctx, "ApiResponse", err, "type", "api data parse", "data", string(baseApi.Data), "bind", fmt.Sprintf("%T", resp))
+ return sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("json.Unmarshal(%q, %T) failed %s", string(baseApi.Data), resp, err.Error()))
+ }
+
+ return nil
+}
+
+// CallApi wraps ApiPost to make an API call and unmarshal the response into a new instance of type T.
+func CallApi[T any](ctx context.Context, api string, req any) (*T, error) {
+ var resp T
+ if err := ApiPost(ctx, api, req, &resp); err != nil {
return nil, err
}
- return b, nil
+ return &resp, nil
}
-// application/json; charset=utf-8
-func Post2Api(url string, data interface{}, token string) (content []byte, err error) {
- c, err := postLogic(url, data, token)
- return c, utils.Wrap(err, " post")
- return retry(url, data, token, 1, 10*time.Second, postLogic)
+// GetPageAll handles pagination for API requests. It iterates over pages of data until all data is retrieved.
+// A is the request type with pagination support, B is the response type, and C is the type of data to be returned.
+// The function fn processes each page of response data to extract a slice of C.
+func GetPageAll[A interface {
+ GetPagination() *sdkws.RequestPagination
+}, B, C any](ctx context.Context, api string, req A, fn func(resp *B) []C) ([]C, error) {
+ if req.GetPagination().ShowNumber <= 0 {
+ req.GetPagination().ShowNumber = 50
+ }
+ var res []C
+ for i := int32(0); ; i++ {
+ req.GetPagination().PageNumber = i + 1
+ memberResp, err := CallApi[B](ctx, api, req)
+ if err != nil {
+ return nil, err
+ }
+ list := fn(memberResp)
+ res = append(res, list...)
+ if len(list) < int(req.GetPagination().ShowNumber) {
+ break
+ }
+ }
+ return res, nil
}
-func Post2ApiForRead(url string, data interface{}, token string) (content []byte, err error) {
- return retry(url, data, token, 3, 10*time.Second, postLogic)
+func GetPageAllWithMaxNum[A interface {
+ GetPagination() *sdkws.RequestPagination
+}, B, C any](ctx context.Context, api string, req A, fn func(resp *B) []C, maxItems int) ([]C, error) {
+ if req.GetPagination().ShowNumber <= 0 {
+ req.GetPagination().ShowNumber = 50
+ }
+ var res []C
+ totalFetched := 0
+ for i := int32(0); ; i++ {
+ req.GetPagination().PageNumber = i + 1
+ memberResp, err := CallApi[B](ctx, api, req)
+ if err != nil {
+ return nil, err
+ }
+ list := fn(memberResp)
+ res = append(res, list...)
+ totalFetched += len(list)
+ if len(list) < int(req.GetPagination().ShowNumber) || (maxItems > 0 && totalFetched >= maxItems) {
+ break
+ }
+ }
+ if maxItems > 0 && len(res) > maxItems {
+ res = res[:maxItems]
+ }
+ return res, nil
}
-func postLogic(url string, data interface{}, token string) (content []byte, err error) {
- jsonStr, err := json.Marshal(data)
- if err != nil {
- return nil, utils.Wrap(err, "marshal failed, url")
+func FetchAndInsertPagedData[RESP, L any](ctx context.Context, api string, req page.PageReq, fn func(resp *RESP) []L, batchInsertFn func(ctx context.Context, items []L) error,
+ insertFn func(ctx context.Context, item L) error, maxItems int64) error {
+ if req.GetPagination().ShowNumber <= 0 {
+ req.GetPagination().ShowNumber = 50
}
+ var errSingle error
+ var errList []error
+ totalFetched := 0
+ for i := int32(0); ; i++ {
+ req.GetPagination().PageNumber = i + 1
+ memberResp, err := CallApi[RESP](ctx, api, req)
+ if err != nil {
+ return err
+ }
+ list := fn(memberResp)
+ if err := batchInsertFn(ctx, list); err != nil {
+ for _, item := range list {
+ errSingle = insertFn(ctx, item)
+ if errSingle != nil {
+ errList = append(errList, errs.New(errSingle.Error(), "item", item))
+ }
+ }
+ }
+ totalFetched += len(list)
+ if len(list) < int(req.GetPagination().ShowNumber) || (maxItems > 0 && totalFetched >= int(maxItems)) {
+ break
+ }
+ }
+ if len(errList) > 0 {
+ return errs.WrapMsg(errList[0], "batch insert failed due to data exception")
+ }
+ return nil
+}
- req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
- if err != nil {
- return nil, utils.Wrap(err, "newRequest failed, url")
- }
- req.Close = true
- req.Header.Add("content-type", "application/json")
- req.Header.Add("token", token)
- req.Header.Add("OperationID", utils.OperationIDGenerator())
- tp := &http.Transport{
- DialContext: (&net.Dialer{
- KeepAlive: 10 * time.Minute,
- }).DialContext,
- ResponseHeaderTimeout: 60 * time.Second,
- MaxIdleConnsPerHost: 100,
- IdleConnTimeout: 60 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- }
- client := &http.Client{Timeout: 60 * time.Second, Transport: tp}
- resp, err := client.Do(req)
- if err != nil {
- return nil, utils.Wrap(err, "client.Do failed, url")
+type pagination interface {
+ GetPagination() *sdkws.RequestPagination
+}
+
+func PageNext[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) {
+ if req.GetPagination() == nil {
+ vof := reflect.ValueOf(req)
+ for {
+ if vof.Kind() == reflect.Ptr {
+ vof = vof.Elem()
+ } else {
+ break
+ }
+ }
+ if vof.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("request is not a struct")
+ }
+ fof := vof.FieldByName("Pagination")
+ if !fof.IsValid() {
+ return nil, fmt.Errorf("request is not valid Pagination field")
+ }
+ fof.Set(reflect.ValueOf(&sdkws.RequestPagination{}))
}
- if resp.StatusCode != 200 {
- return nil, utils.Wrap(errors.New(resp.Status), "status code failed "+url)
+ if req.GetPagination().PageNumber < 0 {
+ req.GetPagination().PageNumber = 0
}
- defer resp.Body.Close()
- result, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, utils.Wrap(err, "ioutil.ReadAll failed, url")
+ if req.GetPagination().ShowNumber <= 0 {
+ req.GetPagination().ShowNumber = 200
+ }
+ var result []Elem
+ for i := int32(0); ; i++ {
+ req.GetPagination().PageNumber = i + 1
+ resp, err := api(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+ elems := fn(resp)
+ result = append(result, elems...)
+ if len(elems) < int(req.GetPagination().ShowNumber) {
+ break
+ }
}
- // fmt.Println(url, "Marshal data: ", string(jsonStr), string(result))
return result, nil
}
diff --git a/pkg/network/http_client_test.go b/pkg/network/http_client_test.go
new file mode 100644
index 000000000..6c0535d4b
--- /dev/null
+++ b/pkg/network/http_client_test.go
@@ -0,0 +1,22 @@
+package network
+
+import (
+ "context"
+
+ "testing"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+)
+
+func TestName(t *testing.T) {
+ var conf ccontext.GlobalConfig
+ conf.ApiAddr = "http://127.0.0.1:8080"
+ ctx := ccontext.WithInfo(context.Background(), &conf)
+ ctx = ccontext.WithOperationID(ctx, "123456")
+ var resp any
+ if err := ApiPost(ctx, "/test", map[string]any{}, &resp); err != nil {
+ t.Log(err)
+ return
+ }
+ t.Log("success")
+}
diff --git a/pkg/network/new_http.go b/pkg/network/new_http.go
index aa68e233c..fb02fdc8d 100644
--- a/pkg/network/new_http.go
+++ b/pkg/network/new_http.go
@@ -18,12 +18,13 @@ import (
"bytes"
"encoding/json"
"errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"io/ioutil"
"net/http"
"net/url"
"time"
"unsafe"
+
+ "github.com/openimsdk/tools/errs"
)
type HttpCli struct {
@@ -54,7 +55,7 @@ func Post(url string) *HttpCli {
return &HttpCli{
httpClient: newHttpClient(),
httpRequest: request,
- Error: utils.Wrap(err, "newRequest failed, url"),
+ Error: errs.WrapMsg(err, "newRequest failed, url"),
}
}
@@ -75,7 +76,7 @@ func (c *HttpCli) BodyWithJson(obj interface{}) *HttpCli {
buf, err := json.Marshal(obj)
if err != nil {
- c.Error = utils.Wrap(err, "marshal failed, url")
+ c.Error = errs.WrapMsg(err, "marshal failed, url")
return c
}
c.httpRequest.Body = ioutil.NopCloser(bytes.NewReader(buf))
@@ -124,16 +125,16 @@ func (c *HttpCli) ToBytes() (content []byte, err error) {
resp, err := c.httpClient.Do(c.httpRequest)
if err != nil {
- return nil, utils.Wrap(err, "client.Do failed, url")
+ return nil, errs.WrapMsg(err, "client.Do failed, url")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- return nil, utils.Wrap(errors.New(resp.Status), "status code failed ")
+ return nil, errs.WrapMsg(errors.New(resp.Status), "status code failed ")
}
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
- return nil, utils.Wrap(err, "ioutil.ReadAll failed, url")
+ return nil, errs.WrapMsg(err, "ioutil.ReadAll failed, url")
}
return buf, nil
@@ -146,20 +147,20 @@ func (c *HttpCli) ToJson(obj interface{}) error {
resp, err := c.httpClient.Do(c.httpRequest)
if err != nil {
- return utils.Wrap(err, "client.Do failed, url")
+ return errs.WrapMsg(err, "client.Do failed, url")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- return utils.Wrap(errors.New(resp.Status), "status code failed ")
+ return errs.WrapMsg(errors.New(resp.Status), "status code failed ")
}
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
- return utils.Wrap(err, "ioutil.ReadAll failed, url")
+ return errs.WrapMsg(err, "ioutil.ReadAll failed, url")
}
err = json.Unmarshal(buf, obj)
if err != nil {
- return utils.Wrap(err, "marshal failed, url")
+ return errs.WrapMsg(err, "marshal failed, url")
}
return nil
}
diff --git a/pkg/sdk_params_callback/conversation_msg_sdk_struct.go b/pkg/sdk_params_callback/conversation_msg_sdk_struct.go
index bf8a53999..7a2aa19fd 100644
--- a/pkg/sdk_params_callback/conversation_msg_sdk_struct.go
+++ b/pkg/sdk_params_callback/conversation_msg_sdk_struct.go
@@ -15,45 +15,43 @@
package sdk_params_callback
import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
)
-type FindMessageListParams []*ConversationArgs
type ConversationArgs struct {
ConversationID string `json:"conversationID"`
ClientMsgIDList []string `json:"clientMsgIDList"`
}
+
type FindMessageListCallback struct {
TotalCount int `json:"totalCount"`
FindResultItems []*SearchByConversationResult `json:"findResultItems"`
}
-type GetHistoryMessageListParams struct {
- UserID string `json:"userID"`
- GroupID string `json:"groupID"`
- ConversationID string `json:"conversationID"`
- StartClientMsgID string `json:"startClientMsgID"`
- Count int `json:"count"`
-}
-type GetHistoryMessageListCallback []*sdk_struct.MsgStruct
+
type GetAdvancedHistoryMessageListParams struct {
- LastMinSeq int64 `json:"lastMinSeq"`
ConversationID string `json:"conversationID"`
StartClientMsgID string `json:"startClientMsgID"`
Count int `json:"count"`
+ ViewType int `json:"viewType"`
}
+
type GetAdvancedHistoryMessageListCallback struct {
MessageList []*sdk_struct.MsgStruct `json:"messageList"`
- LastMinSeq int64 `json:"lastMinSeq"`
IsEnd bool `json:"isEnd"`
ErrCode int32 `json:"errCode"`
ErrMsg string `json:"errMsg"`
}
-type SetConversationStatusParams struct {
- UserId string `json:"userID" validate:"required"`
- Status int `json:"status" validate:"required"`
+type FetchSurroundingMessagesReq struct {
+ StartMessage *sdk_struct.MsgStruct `json:"startMessage"`
+ ViewType int `json:"viewType"`
+ Before int `json:"before"`
+ After int `json:"after"`
+}
+type FetchSurroundingMessagesResp struct {
+ MessageList []*sdk_struct.MsgStruct `json:"messageList"`
}
+
type SearchLocalMessagesParams struct {
ConversationID string `json:"conversationID"`
KeywordList []string `json:"keywordList"`
@@ -65,10 +63,12 @@ type SearchLocalMessagesParams struct {
PageIndex int `json:"pageIndex"`
Count int `json:"count"`
}
+
type SearchLocalMessagesCallback struct {
TotalCount int `json:"totalCount"`
SearchResultItems []*SearchByConversationResult `json:"searchResultItems"`
}
+
type SearchByConversationResult struct {
ConversationID string `json:"conversationID"`
ConversationType int32 `json:"conversationType"`
@@ -78,43 +78,3 @@ type SearchByConversationResult struct {
MessageCount int `json:"messageCount"`
MessageList []*sdk_struct.MsgStruct `json:"messageList"`
}
-type SetMessageReactionExtensionsParams []*server_api_params.KeyValue
-
-type SetMessageReactionExtensionsCallback struct {
- Key string `json:"key" validate:"required"`
- Value string `json:"value" validate:"required"`
- ErrCode int32 `json:"errCode"`
- ErrMsg string `json:"errMsg"`
-}
-
-type AddMessageReactionExtensionsParams []*server_api_params.KeyValue
-
-type AddMessageReactionExtensionsCallback struct {
- Key string `json:"key" validate:"required"`
- Value string `json:"value" validate:"required"`
- ErrCode int32 `json:"errCode"`
- ErrMsg string `json:"errMsg"`
-}
-type DeleteMessageReactionExtensionsParams []string
-
-type GetTypekeyListResp struct {
- TypeKeyInfoList []*SingleTypeKeyInfoSum `json:"TypeKeyListInfo"`
-}
-type SingleTypeKeyInfoSum struct {
- TypeKey string `json:"typeKey"`
- Counter int64 `json:"counter"`
- InfoList []*Info `json:"infoList"`
- IsContainSelf bool `json:"isContainSelf"`
-}
-
-type SingleTypeKeyInfo struct {
- TypeKey string `json:"typeKey"`
- Counter int64 `json:"counter"`
- IsCanRepeat bool `json:"isCanRepeat"`
- Index int `json:"index"`
- InfoList map[string]*Info `json:"infoList"`
-}
-type Info struct {
- UserID string `json:"userID"`
- Ex string `json:"ex"`
-}
diff --git a/pkg/sdk_params_callback/friend_sdk_struct.go b/pkg/sdk_params_callback/friend_sdk_struct.go
index e58e0b24f..c6e36f3e8 100644
--- a/pkg/sdk_params_callback/friend_sdk_struct.go
+++ b/pkg/sdk_params_callback/friend_sdk_struct.go
@@ -15,54 +15,15 @@
package sdk_params_callback
import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
"github.com/openimsdk/protocol/wrapperspb"
)
-// 1
-type GetDesignatedFriendsInfoParams []string
-type GetDesignatedFriendsInfoCallback []server_api_params.FullUserInfo
-
-// 1
-type AddFriendParams struct {
- ToUserID string `json:"toUserID" validate:"required"`
- ReqMsg string `json:"reqMsg"`
-}
-
-const AddFriendCallback = constant.SuccessCallbackDefault
-
-// 1
-// type GetRecvFriendApplicationListParams struct{}
-type GetRecvFriendApplicationListCallback []*model_struct.LocalFriendRequest
-
-// 1
-// type GetSendFriendApplicationListParams struct{}
-type GetSendFriendApplicationListCallback []*model_struct.LocalFriendRequest
-
-// 1
type ProcessFriendApplicationParams struct {
ToUserID string `json:"toUserID" validate:"required"`
HandleMsg string `json:"handleMsg"`
}
-const ProcessFriendApplicationCallback = constant.SuccessCallbackDefault
-
-// 1
-type CheckFriendParams []string
-type CheckFriendCallback []server_api_params.UserIDResult
-
-// 1
-type DeleteFriendParams string
-
-// type DeleteFriendCallback struct{}
-const DeleteFriendCallback = constant.SuccessCallbackDefault
-
-// 1
-// type GetFriendListParams struct{}
-type GetFriendListCallback []server_api_params.FullUserInfo
-
type SearchFriendsParam struct {
KeywordList []string `json:"keywordList"`
IsSearchUserID bool `json:"isSearchUserID"`
@@ -70,16 +31,11 @@ type SearchFriendsParam struct {
IsSearchRemark bool `json:"isSearchRemark"`
}
-type GetFriendListPage struct {
-}
-
-type SearchFriendsCallback []*SearchFriendItem
type SearchFriendItem struct {
model_struct.LocalFriend
Relationship int `json:"relationship"`
}
-// 1
type SetFriendRemarkParams struct {
ToUserID string `json:"toUserID" validate:"required"`
Remark string `json:"remark" validate:"required"`
@@ -88,22 +44,3 @@ type SetFriendPinParams struct {
ToUserIDs []string `json:"toUserIDs" validate:"required"`
IsPinned *wrapperspb.BoolValue `json:"isPinned" validate:"required"`
}
-
-// type SetFriendRemarkCallback struct{}
-const SetFriendRemarkCallback = constant.SuccessCallbackDefault
-
-// 1
-type AddBlackParams string
-
-// type AddBlackCallback struct{}
-const AddBlackCallback = constant.SuccessCallbackDefault
-
-// 1
-// type GetBlackListParams struct{}
-type GetBlackListCallback []server_api_params.FullUserInfo
-
-// 1
-type RemoveBlackParams string
-
-// type DeleteBlackCallback struct{}
-const RemoveBlackCallback = constant.SuccessCallbackDefault
diff --git a/pkg/sdk_params_callback/group_sdk_struct.go b/pkg/sdk_params_callback/group_sdk_struct.go
index 200e296b1..ac4f9292c 100644
--- a/pkg/sdk_params_callback/group_sdk_struct.go
+++ b/pkg/sdk_params_callback/group_sdk_struct.go
@@ -14,100 +14,18 @@
package sdk_params_callback
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
-)
-
-type CreateGroupBaseInfoParam struct {
- GroupType int32 `json:"groupType"`
- SetGroupInfoParam
-}
-
-type CreateGroupMemberRoleParam []*server_api_params.GroupAddMemberInfo
-type CreateGroupCallback map[string]interface{}
-
-// param groupID reqMsg
-const JoinGroupCallback = constant.SuccessCallbackDefault
-
-// type QuitGroupParam // groupID
-const QuitGroupCallback = constant.SuccessCallbackDefault
-
-const DismissGroupCallback = constant.SuccessCallbackDefault
-
-const GroupMuteChangeCallback = constant.SuccessCallbackDefault
-
-const GroupMemberMuteChangeCallback = constant.SuccessCallbackDefault
-
-const SetGroupMemberNicknameCallback = constant.SuccessCallbackDefault
-
-// type GetJoinedGroupListParam null
-type GetJoinedGroupListCallback []*model_struct.LocalGroup
-
-type GetGroupsInfoParam []string
-type GetGroupsInfoCallback []*model_struct.LocalGroup
type SearchGroupsParam struct {
KeywordList []string `json:"keywordList"`
IsSearchGroupID bool `json:"isSearchGroupID"`
IsSearchGroupName bool `json:"isSearchGroupName"`
}
-type SearchGroupsCallback []*model_struct.LocalGroup
type SearchGroupMembersParam struct {
GroupID string `json:"groupID"`
KeywordList []string `json:"keywordList"`
IsSearchUserID bool `json:"isSearchUserID"`
IsSearchMemberNickname bool `json:"isSearchMemberNickname"`
- //offset, count int
- Offset int `json:"offset"`
- Count int `json:"count"`
- PageNumber int `json:"pageNumber"`
-}
-
-type SearchGroupMembersCallback []*model_struct.LocalGroupMember
-
-type SetGroupInfoParam struct {
- GroupName string `json:"groupName"`
- Notification string `json:"notification"`
- Introduction string `json:"introduction"`
- FaceURL string `json:"faceURL"`
- Ex string `json:"ex"`
- NeedVerification *int32 `json:"needVerification" binding:"oneof=0 1 2"`
+ Offset int `json:"offset"`
+ Count int `json:"count"`
+ PageNumber int `json:"pageNumber"`
}
-
-type SetGroupMemberInfoParam struct {
- GroupID string `json:"groupID"`
- UserID string `json:"userID"`
- Ex *string `json:"ex"`
-}
-
-const SetGroupMemberInfoCallback = constant.SuccessCallbackDefault
-
-const SetGroupInfoCallback = constant.SuccessCallbackDefault
-
-// type GetGroupMemberListParam groupID ...
-type GetGroupMemberListCallback []*model_struct.LocalGroupMember
-
-type GetGroupMembersInfoParam []string
-type GetGroupMembersInfoCallback []*model_struct.LocalGroupMember
-
-type KickGroupMemberParam []string
-type KickGroupMemberCallback []*server_api_params.UserIDResult
-
-// type TransferGroupOwnerParam
-const TransferGroupOwnerCallback = constant.SuccessCallbackDefault
-
-type InviteUserToGroupParam []string
-type InviteUserToGroupCallback []*server_api_params.UserIDResult
-
-// type GetGroupApplicationListParam
-type GetGroupApplicationListCallback []*model_struct.LocalAdminGroupRequest
-
-type GetSendGroupApplicationListCallback []*model_struct.LocalGroupRequest
-
-// type AcceptGroupApplicationParam
-const AcceptGroupApplicationCallback = constant.SuccessCallbackDefault
-
-// type RefuseGroupApplicationParam
-const RefuseGroupApplicationCallback = constant.SuccessCallbackDefault
diff --git a/pkg/sdk_params_callback/listener_callback.go b/pkg/sdk_params_callback/listener_callback.go
deleted file mode 100644
index fca1c92ce..000000000
--- a/pkg/sdk_params_callback/listener_callback.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package sdk_params_callback
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-)
-
-// //////////////////////////////friend////////////////////////////////////
-type FriendApplicationAddedCallback model_struct.LocalFriendRequest
-type FriendApplicationAcceptCallback model_struct.LocalFriendRequest
-type FriendApplicationRejectCallback model_struct.LocalFriendRequest
-type FriendApplicationDeletedCallback model_struct.LocalFriendRequest
-type FriendAddedCallback model_struct.LocalFriend
-type FriendDeletedCallback model_struct.LocalFriend
-type FriendInfoChangedCallback model_struct.LocalFriend
-type BlackAddCallback model_struct.LocalBlack
-type BlackDeletedCallback model_struct.LocalBlack
-
-////////////////////////////////group////////////////////////////////////
-
-type JoinedGroupAddedCallback model_struct.LocalGroup
-type JoinedGroupDeletedCallback model_struct.LocalGroup
-type GroupMemberAddedCallback model_struct.LocalGroupMember
-type GroupMemberDeletedCallback model_struct.LocalGroupMember
-type GroupApplicationAddedCallback model_struct.LocalAdminGroupRequest
-type GroupApplicationDeletedCallback model_struct.LocalAdminGroupRequest
-type GroupApplicationAcceptCallback model_struct.LocalAdminGroupRequest
-type GroupApplicationRejectCallback model_struct.LocalAdminGroupRequest
-type GroupInfoChangedCallback model_struct.LocalGroup
-type GroupMemberInfoChangedCallback model_struct.LocalGroupMember
-
-// ////////////////////////////user////////////////////////////////////////
-type SelfInfoUpdatedCallback model_struct.LocalUser
-
-// ////////////////////////////user////////////////////////////////////////
-type ConversationUpdateCallback model_struct.LocalConversation
-type ConversationDeleteCallback model_struct.LocalConversation
-
-// ///////////////////////////signaling/////////////////////////////////////
-type InvitationInfo struct {
- InviterUserID string
- InviteeUserIDList []string
- CustomData string
- GroupID string
-}
-
-//type ReceiveNewInvitationCallback sdkws.SignalInviteReq
-//
-//type InviteeAcceptedCallback sdkws.SignalAcceptReq
-//
-//type InviteeRejectedCallback sdkws.SignalRejectReq
-//
-//type InvitationCancelledCallback sdkws.SignalCancelReq
-//
-//type InvitationTimeoutCallback sdkws.SignalInviteReq
diff --git a/pkg/sdk_params_callback/third_sdk_struct.go b/pkg/sdk_params_callback/third_sdk_struct.go
deleted file mode 100755
index 327d76c84..000000000
--- a/pkg/sdk_params_callback/third_sdk_struct.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package sdk_params_callback
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
-)
-
-const UpdateFcmTokenCallback = constant.SuccessCallbackDefault
-const SetAppBadgeCallback = constant.SuccessCallbackDefault
-
-type UploadLogParams struct {
- SystemType string `json:"system_type"`
- Ex string `json:"ex"`
-}
diff --git a/pkg/sdk_params_callback/user_sdk_struct.go b/pkg/sdk_params_callback/user_sdk_struct.go
deleted file mode 100644
index 3fff3372f..000000000
--- a/pkg/sdk_params_callback/user_sdk_struct.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package sdk_params_callback
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
-)
-
-// other user
-type GetUsersInfoParam []string
-type GetUsersInfoCallback []server_api_params.FullUserInfo
-
-// type GetSelfUserInfoParam string
-type GetSelfUserInfoCallback *model_struct.LocalUser
-
-type SetSelfUserInfoParam server_api_params.ApiUserInfo
-
-const SetSelfUserInfoCallback = constant.SuccessCallbackDefault
diff --git a/pkg/sdkerrs/code.go b/pkg/sdkerrs/code.go
index 6942eaee3..f2c19935b 100644
--- a/pkg/sdkerrs/code.go
+++ b/pkg/sdkerrs/code.go
@@ -14,39 +14,39 @@
package sdkerrs
-// 通用错误码
+// Common error codes
const (
NetworkError = 10000
NetworkTimeoutError = 10001
- ArgsError = 10002 //输入参数错误
- CtxDeadlineExceededError = 10003 //上下文超时
-
- ResourceLoadNotCompleteError = 10004 //资源初始化未完成
- UnknownCode = 10005 //没有解析到code
- SdkInternalError = 10006 //SDK内部错误
-
- NoUpdateError = 10007 //没有更新
-
- UserIDNotFoundError = 10100 //UserID不存在 或未注册
- LoginOutError = 10101 //用户已经退出登录
- LoginRepeatError = 10102 //用户重复登录
-
- //消息相关
- FileNotFoundError = 10200 //记录不存在
- MsgDeCompressionError = 10201 //消息解压失败
- MsgDecodeBinaryWsError = 10202 //消息解码失败
- MsgBinaryTypeNotSupportError = 10203 //消息类型不支持
- MsgRepeatError = 10204 //消息重复发送
- MsgContentTypeNotSupportError = 10205 //消息类型不支持
- MsgHasNoSeqError = 10206 //消息没有seq
-
- //会话相关
- NotSupportOptError = 10301 //不支持的操作
- NotSupportTypeError = 10302 //not support type
- UnreadCountError = 10303 //unread count has zero
-
- //群组相关
- GroupIDNotFoundError = 10400 //GroupID不存在
- GroupTypeErr = 10401 //群组类型错误
-
+ ArgsError = 10002 // Invalid input parameters
+ CtxDeadlineExceededError = 10003 // Context deadline exceeded
+
+ ResourceLoadNotCompleteError = 10004 // Resource initialization incomplete
+ UnknownCode = 10005 // Unrecognized code
+ SdkInternalError = 10006 // SDK internal error
+
+ NoUpdateError = 10007 // No updates available
+
+ UserIDNotFoundError = 10100 // UserID not found or not registered
+ LoginOutError = 10101 // User has logged out
+ LoginRepeatError = 10102 // User logged in repeatedly
+
+ // Message-related errors
+ FileNotFoundError = 10200 // Record not found
+ MsgDeCompressionError = 10201 // Message decompression failed
+ MsgDecodeBinaryWsError = 10202 // Message decoding failed
+ MsgBinaryTypeNotSupportError = 10203 // Message type not supported
+ MsgRepeatError = 10204 // Message repeated
+ MsgContentTypeNotSupportError = 10205 // Message content type not supported
+ MsgHasNoSeqError = 10206 // Message does not have a sequence number
+ MsgHasDeletedError = 10207 // Message has been deleted
+
+ // Conversation-related errors
+ NotSupportOptError = 10301 // Operation not supported
+ NotSupportTypeError = 10302 // Type not supported
+ UnreadCountError = 10303 // Unread count is zero
+
+ // Group-related errors
+ GroupIDNotFoundError = 10400 // GroupID not found
+ GroupTypeErr = 10401 // Invalid group type
)
diff --git a/pkg/sdkerrs/predefine.go b/pkg/sdkerrs/predefine.go
index 076d1fbdc..97a5aaea8 100644
--- a/pkg/sdkerrs/predefine.go
+++ b/pkg/sdkerrs/predefine.go
@@ -17,34 +17,36 @@ package sdkerrs
import "github.com/openimsdk/tools/errs"
var (
- ErrArgs = errs.NewCodeError(ArgsError, "ArgsError")
- ErrCtxDeadline = errs.NewCodeError(CtxDeadlineExceededError, "CtxDeadlineExceededError")
- ErrSdkInternal = errs.NewCodeError(SdkInternalError, "SdkInternalError")
- ErrNetwork = errs.NewCodeError(NetworkError, "NetworkError")
- ErrNetworkTimeOut = errs.NewCodeError(NetworkTimeoutError, "NetworkTimeoutError")
-
- ErrGroupIDNotFound = errs.NewCodeError(GroupIDNotFoundError, "GroupIDNotFoundError")
- ErrUserIDNotFound = errs.NewCodeError(UserIDNotFoundError, "UserIDNotFoundError")
-
- ErrResourceLoad = errs.NewCodeError(ResourceLoadNotCompleteError, "ResourceLoadNotCompleteError")
-
- //消息相关
- ErrFileNotFound = errs.NewCodeError(FileNotFoundError, "RecordNotFoundError")
- ErrMsgDecodeBinaryWs = errs.NewCodeError(MsgDecodeBinaryWsError, "MsgDecodeBinaryWsError")
- ErrMsgDeCompression = errs.NewCodeError(MsgDeCompressionError, "MsgDeCompressionError")
- ErrMsgBinaryTypeNotSupport = errs.NewCodeError(MsgBinaryTypeNotSupportError, "MsgTypeNotSupportError")
- ErrMsgRepeated = errs.NewCodeError(MsgRepeatError, "only failed message can be repeatedly send")
- ErrMsgContentTypeNotSupport = errs.NewCodeError(MsgContentTypeNotSupportError, "contentType not support currently") // msg // msg
- ErrMsgHasNoSeq = errs.NewCodeError(MsgHasNoSeqError, "msg has no seq") // msg // msg
-
- //会话相关
- ErrNotSupportOpt = errs.NewCodeError(NotSupportOptError, "super group not support this opt")
- ErrNotSupportType = errs.NewCodeError(NotSupportTypeError, "only support super group type type")
- ErrUnreadCount = errs.NewCodeError(UnreadCountError, "unread count has zero")
- //群组相关
-
- ErrGroupType = errs.NewCodeError(GroupTypeErr, "group type error")
-
- ErrLoginOut = errs.NewCodeError(LoginOutError, "LoginOutError")
- ErrLoginRepeat = errs.NewCodeError(LoginRepeatError, "LoginRepeatError")
+ // Common errors
+ ErrArgs = errs.NewCodeError(ArgsError, "Invalid input arguments")
+ ErrCtxDeadline = errs.NewCodeError(CtxDeadlineExceededError, "Context deadline exceeded")
+ ErrSdkInternal = errs.NewCodeError(SdkInternalError, "Internal SDK error")
+ ErrNetwork = errs.NewCodeError(NetworkError, "Network error")
+ ErrNetworkTimeOut = errs.NewCodeError(NetworkTimeoutError, "Network timeout error")
+
+ ErrGroupIDNotFound = errs.NewCodeError(GroupIDNotFoundError, "Group ID not found")
+ ErrUserIDNotFound = errs.NewCodeError(UserIDNotFoundError, "User ID not found")
+
+ ErrResourceLoad = errs.NewCodeError(ResourceLoadNotCompleteError, "Resource initialization incomplete")
+
+ // Message-related errors
+ ErrFileNotFound = errs.NewCodeError(FileNotFoundError, "File not found")
+ ErrMsgDecodeBinaryWs = errs.NewCodeError(MsgDecodeBinaryWsError, "Message binary WebSocket decoding failed")
+ ErrMsgDeCompression = errs.NewCodeError(MsgDeCompressionError, "Message decompression failed")
+ ErrMsgBinaryTypeNotSupport = errs.NewCodeError(MsgBinaryTypeNotSupportError, "Message type not supported")
+ ErrMsgRepeated = errs.NewCodeError(MsgRepeatError, "Only failed messages can be resent")
+ ErrMsgContentTypeNotSupport = errs.NewCodeError(MsgContentTypeNotSupportError, "Message content type not supported")
+ ErrMsgHasNoSeq = errs.NewCodeError(MsgHasNoSeqError, "Message has no sequence number")
+ ErrMsgHasDeleted = errs.NewCodeError(MsgHasDeletedError, "Message has been deleted")
+
+ // Conversation-related errors
+ ErrNotSupportOpt = errs.NewCodeError(NotSupportOptError, "Operation not supported for supergroup")
+ ErrNotSupportType = errs.NewCodeError(NotSupportTypeError, "Only supergroup type supported")
+ ErrUnreadCount = errs.NewCodeError(UnreadCountError, "Unread count is zero")
+
+ // Group-related errors
+ ErrGroupType = errs.NewCodeError(GroupTypeErr, "Invalid group type")
+
+ ErrLoginOut = errs.NewCodeError(LoginOutError, "User has logged out")
+ ErrLoginRepeat = errs.NewCodeError(LoginRepeatError, "User has logged in repeatedly")
)
diff --git a/pkg/server_api_params/auth_api_struct.go b/pkg/server_api_params/auth_api_struct.go
deleted file mode 100644
index 9868e3e0a..000000000
--- a/pkg/server_api_params/auth_api_struct.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-import "github.com/openimsdk/protocol/sdkws"
-
-//UserID string `protobuf:"bytes,1,opt,name=UserID" json:"UserID,omitempty"`
-// Nickname string `protobuf:"bytes,2,opt,name=Nickname" json:"Nickname,omitempty"`
-// FaceUrl string `protobuf:"bytes,3,opt,name=FaceUrl" json:"FaceUrl,omitempty"`
-// Gender int32 `protobuf:"varint,4,opt,name=Gender" json:"Gender,omitempty"`
-// PhoneNumber string `protobuf:"bytes,5,opt,name=PhoneNumber" json:"PhoneNumber,omitempty"`
-// Birth string `protobuf:"bytes,6,opt,name=Birth" json:"Birth,omitempty"`
-// Email string `protobuf:"bytes,7,opt,name=Email" json:"Email,omitempty"`
-// Ex string `protobuf:"bytes,8,opt,name=Ex" json:"Ex,omitempty"`
-
-type UserRegisterReq struct {
- Secret string `json:"secret" binding:"required,max=32"`
- Platform int32 `json:"platform" binding:"required,min=1,max=7"`
- sdkws.UserInfo
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type UserTokenInfo struct {
- UserID string `json:"userID"`
- Token string `json:"token"`
- ExpiredTime int64 `json:"expiredTime"`
-}
-type UserRegisterResp struct {
- CommResp
- UserToken UserTokenInfo `json:"data"`
-}
-
-type UserTokenReq struct {
- Secret string `json:"secret" binding:"required,max=32"`
- Platform int32 `json:"platformID" binding:"required,min=1,max=8"`
- UserID string `json:"userID" binding:"required,min=1,max=64"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type UserTokenResp struct {
- CommResp
- UserToken UserTokenInfo `json:"data"`
-}
-
-type ParseTokenReq struct {
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type ExpireTime struct {
- ExpireTimeSeconds uint32 `json:"expireTimeSeconds" `
-}
-
-type ParseTokenResp struct {
- CommResp
- ExpireTime ExpireTime `json:"expireTime"`
-}
diff --git a/pkg/server_api_params/aws_api_struct.go b/pkg/server_api_params/aws_api_struct.go
deleted file mode 100644
index ef792eac3..000000000
--- a/pkg/server_api_params/aws_api_struct.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type AwsStorageCredentialReq struct {
- OperationID string `json:"operationID"`
-}
-
-type AwsStorageCredentialResp struct {
- CommResp
- AccessKeyId string `json:"accessKeyID"`
- SecretAccessKey string `json:"secretAccessKey"`
- SessionToken string `json:"sessionToken"`
- RegionID string `json:"regionId"`
- Bucket string `json:"bucket"`
- FinalHost string `json:"FinalHost"`
-}
diff --git a/pkg/server_api_params/chat_api_struct.go b/pkg/server_api_params/chat_api_struct.go
deleted file mode 100644
index e5549088c..000000000
--- a/pkg/server_api_params/chat_api_struct.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type DeleteMsgReq struct {
- OpUserID string `json:"opUserID"`
- UserID string `json:"userID"`
- SeqList []int64 `json:"seqList"`
- OperationID string `json:"operationID"`
-}
-
-type DeleteMsgResp struct {
-}
-
-type CleanUpMsgReq struct {
- UserID string `json:"userID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type CleanUpMsgResp struct {
- CommResp
-}
-type DelSuperGroupMsgReq struct {
- UserID string `json:"userID,omitempty" binding:"required"`
- GroupID string `json:"groupID,omitempty" binding:"required"`
- SeqList []int64 `json:"seqList,omitempty"`
- IsAllDelete bool `json:"isAllDelete"`
- OperationID string `json:"operationID,omitempty" binding:"required"`
-}
-type DelSuperGroupMsgResp struct {
- CommResp
-}
-type MsgDeleteNotificationElem struct {
- GroupID string `json:"groupID"`
- IsAllDelete bool `json:"isAllDelete"`
- SeqList []int64 `json:"seqList"`
-}
-type SetMessageReactionExtensionsReq struct {
- OperationID string `json:"operationID" validate:"required"`
- ClientMsgID string `json:"clientMsgID" validate:"required"`
- SourceID string `json:"sourceID" validate:"required"`
- SessionType int32 `json:"sessionType" validate:"required"`
- ReactionExtensionList map[string]*KeyValue `json:"reactionExtensionList"`
- IsReact bool `json:"isReact,omitempty"`
- IsExternalExtensions bool `json:"isExternalExtensions,omitempty"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime,omitempty"`
-}
-type AddMessageReactionExtensionsReq struct {
- OperationID string `json:"operationID" validate:"required"`
- ClientMsgID string `json:"clientMsgID" validate:"required"`
- SourceID string `json:"sourceID" validate:"required"`
- SessionType int32 `json:"sessionType" validate:"required"`
- ReactionExtensionList map[string]*KeyValue `json:"reactionExtensionList"`
- IsReact bool `json:"isReact,omitempty"`
- IsExternalExtensions bool `json:"isExternalExtensions,omitempty"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime,omitempty"`
- Seq int64 `json:"seq"`
-}
-type DeleteMessageReactionExtensionsReq struct {
- OperationID string `json:"operationID" binding:"required"`
- SourceID string `json:"sourceID" binding:"required"`
- SessionType int32 `json:"sessionType" binding:"required"`
- ClientMsgID string `json:"clientMsgID" binding:"required"`
- IsExternalExtensions bool `json:"isExternalExtensions"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime" binding:"required"`
- ReactionExtensionList []*KeyValue `json:"reactionExtensionList" binding:"required"`
-}
-type DeleteMessageReactionExtensionsResp struct {
- CommResp
- Result []*ExtensionResult
- Data map[string]interface{} `json:"data"`
-}
-type KeyValue struct {
- TypeKey string `json:"typeKey" validate:"required"`
- Value string `json:"value" validate:"required"`
- LatestUpdateTime int64 `json:"latestUpdateTime"`
-}
-type ApiResult struct {
- Result []*ExtensionResult `json:"result"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
- IsReact bool `json:"isReact"`
-}
-type SetMessageReactionExtensionsResp struct {
- CommResp
- ApiResult struct {
- Result []*ExtensionResult `json:"result"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
- IsReact bool `json:"isReact"`
- }
- Data map[string]interface{} `json:"data"`
-}
-type AddMessageReactionExtensionsResp struct {
- CommResp
- ApiResult struct {
- Result []*ExtensionResult `json:"result"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
- IsReact bool `json:"isReact"`
- }
- Data map[string]interface{} `json:"data"`
-}
-type ExtensionResult struct {
- CommResp
- KeyValue
-}
-
-type GetMessageListReactionExtensionsReq struct {
- OperationID string `json:"operationID" binding:"required"`
- SourceID string `json:"sourceID" binding:"required"`
- SessionType int32 `json:"sessionType" binding:"required"`
- IsExternalExtensions bool `json:"isExternalExtensions"`
- TypeKeyList []string `json:"typeKeyList"`
- MessageReactionKeyList []OperateMessageListReactionExtensionsReq `json:"messageReactionKeyList" binding:"required"`
-}
-
-type KeyValueResp struct {
- KeyValue *KeyValue `protobuf:"bytes,1,opt,name=keyValue" json:"keyValue,omitempty"`
- ErrCode int32 `protobuf:"varint,2,opt,name=errCode" json:"errCode,omitempty"`
- ErrMsg string `protobuf:"bytes,3,opt,name=errMsg" json:"errMsg,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-//type ExtendMsg struct {
-// ReactionExtensionList map[string]*KeyValueResp `protobuf:"bytes,1,rep,name=reactionExtensionList" json:"reactionExtensionList,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
-// ClientMsgID string `protobuf:"bytes,2,opt,name=clientMsgID" json:"clientMsgID,omitempty"`
-// MsgFirstModifyTime int64 `protobuf:"varint,3,opt,name=msgFirstModifyTime" json:"msgFirstModifyTime,omitempty"`
-// AttachedInfo string `protobuf:"bytes,4,opt,name=attachedInfo" json:"attachedInfo,omitempty"`
-// Ex string `protobuf:"bytes,5,opt,name=ex" json:"ex,omitempty"`
-//}
-//
-//type ExtendMsgResp struct {
-// ExtendMsg *ExtendMsg `protobuf:"bytes,1,opt,name=extendMsg" json:"extendMsg,omitempty"`
-// ErrCode int32 `protobuf:"varint,2,opt,name=errCode" json:"errCode,omitempty"`
-// ErrMsg string `protobuf:"bytes,3,opt,name=errMsg" json:"errMsg,omitempty"`
-//}
-
-type GetMessageListReactionExtensionsResp []*SingleMessageExtensionResult
-
-type OperateMessageListReactionExtensionsReq struct {
- ClientMsgID string `json:"clientMsgID"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
-}
-type ReactionMessageModifierNotification struct {
- Operation int `json:"operation" binding:"required"`
- SourceID string `json:"sourceID" binding:"required"`
- OpUserID string `json:"opUserID" binding:"required"`
- SessionType int32 `json:"sessionType" binding:"required"`
- // SuccessReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList,omitempty" binding:"required"`
- ClientMsgID string `json:"clientMsgID" binding:"required"`
- IsReact bool `json:"isReact"`
- IsExternalExtensions bool `json:"isExternalExtensions"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
- Seq uint32 `json:"seq"`
-}
-type SingleMessageExtensionResult struct {
- ErrCode int32 `protobuf:"varint,1,opt,name=errCode" json:"errCode,omitempty"`
- ErrMsg string `protobuf:"bytes,2,opt,name=errMsg" json:"errMsg,omitempty"`
- // ReactionExtensionList map[string]*sdkws.KeyValue `protobuf:"bytes,3,rep,name=reactionExtensionList" json:"reactionExtensionList,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
- ClientMsgID string `protobuf:"bytes,4,opt,name=clientMsgID" json:"clientMsgID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-type ReactionMessageDeleteNotification struct {
- SourceID string `json:"sourceID" binding:"required"`
- OpUserID string `json:"opUserID" binding:"required"`
- SessionType int32 `json:"sessionType" binding:"required"`
- // SuccessReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList,omitempty" binding:"required"`
- ClientMsgID string `json:"clientMsgID" binding:"required"`
- MsgFirstModifyTime int64 `json:"msgFirstModifyTime"`
-}
diff --git a/pkg/server_api_params/conversation_api_struct.go b/pkg/server_api_params/conversation_api_struct.go
index 754202349..56c6e4759 100644
--- a/pkg/server_api_params/conversation_api_struct.go
+++ b/pkg/server_api_params/conversation_api_struct.go
@@ -32,67 +32,3 @@ type Conversation struct {
AttachedInfo string `json:"attachedInfo"`
Ex string `json:"ex"`
}
-
-type SetConversationReq struct {
- Conversation
- NotificationType int `json:"notificationType"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type SetConversationResp struct {
-}
-type ModifyConversationFieldReq struct {
- Conversation
- FieldType int32 `json:"fieldType" binding:"required"`
- UserIDList []string `json:"userIDList" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type ModifyConversationFieldResp struct {
- CommResp
-}
-type BatchSetConversationsReq struct {
- Conversations []Conversation `json:"conversations" binding:"required"`
- OwnerUserID string `json:"ownerUserID" binding:"required"`
- NotificationType int `json:"notificationType"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type BatchSetConversationsResp struct {
- Success []string `json:"success"`
- Failed []string `json:"failed"`
-}
-
-type GetConversationReq struct {
- ConversationID string `json:"conversationID" binding:"required"`
- OwnerUserID string `json:"ownerUserID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type GetConversationResp struct {
- Conversation Conversation `json:"data"`
-}
-
-type GetAllConversationsReq struct {
- OwnerUserID string `json:"ownerUserID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type GetAllConversationsResp struct {
- Conversations []Conversation `json:"data"`
-}
-
-type GetConversationsReq struct {
- ConversationIDs []string `json:"conversationIDs" binding:"required"`
- OwnerUserID string `json:"ownerUserID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type GetConversationsResp struct {
- CommResp
- Conversations []Conversation `json:"data"`
-}
-
-type GetConversationRecvMessageOptResp struct {
- ConversationID string `json:"conversationID"`
- Result *int32 `json:"result"`
-}
diff --git a/pkg/server_api_params/friend_api_struct.go b/pkg/server_api_params/friend_api_struct.go
index 7bfa3b871..054890b56 100644
--- a/pkg/server_api_params/friend_api_struct.go
+++ b/pkg/server_api_params/friend_api_struct.go
@@ -14,137 +14,7 @@
package server_api_params
-import "github.com/openimsdk/protocol/sdkws"
-
-type ParamsCommFriend struct {
- OperationID string `json:"operationID" binding:"required"`
- ToUserID string `json:"toUserID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-
-type AddBlacklistReq struct {
- ParamsCommFriend
-}
-type AddBlacklistResp struct {
- CommResp
-}
-
-type ImportFriendReq struct {
- FriendUserIDList []string `json:"friendUserIDList" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
type UserIDResult struct {
- UserID string `json:"userID""`
+ UserID string `json:"userID"`
Result int32 `json:"result"`
}
-type ImportFriendResp struct {
- CommResp
- UserIDResultList []UserIDResult `json:"data"`
-}
-
-type AddFriendReq struct {
- ParamsCommFriend
- ReqMsg string `json:"reqMsg"`
-}
-type AddFriendResp struct {
- CommResp
-}
-
-type AddFriendResponseReq struct {
- ParamsCommFriend
- Flag int32 `json:"flag" binding:"required,oneof=-1 0 1"`
- HandleMsg string `json:"handleMsg"`
-}
-type AddFriendResponseResp struct {
- CommResp
-}
-
-type DeleteFriendReq struct {
- ParamsCommFriend
-}
-type DeleteFriendResp struct {
- CommResp
-}
-
-type GetBlackListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-type GetBlackListResp struct {
- CommResp
- BlackUserInfoList []*sdkws.PublicUserInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-//type PublicUserInfo struct {
-// UserID string `json:"userID"`
-// Nickname string `json:"nickname"`
-// FaceUrl string `json:"faceUrl"`
-// Gender int32 `json:"gender"`
-//}
-
-type SetFriendRemarkReq struct {
- ParamsCommFriend
- Remark string `json:"remark" binding:"required"`
-}
-type SetFriendRemarkResp struct {
- CommResp
-}
-
-type RemoveBlackListReq struct {
- ParamsCommFriend
-}
-type RemoveBlackListResp struct {
- CommResp
-}
-
-type IsFriendReq struct {
- ParamsCommFriend
-}
-type Response struct {
- Friend bool `json:"isFriend"`
-}
-type IsFriendResp struct {
- CommResp
- Response Response `json:"data"`
-}
-
-type GetFriendsInfoReq struct {
- ParamsCommFriend
-}
-type GetFriendsInfoResp struct {
- CommResp
- FriendInfoList []*sdkws.FriendInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetFriendListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-type GetFriendListResp struct {
- CommResp
- FriendInfoList []*sdkws.FriendInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetFriendApplyListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-type GetFriendApplyListResp struct {
- CommResp
- FriendRequestList []*sdkws.FriendRequest
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetSelfFriendApplyListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-type GetSelfFriendApplyListResp struct {
- CommResp
- FriendRequestList []*sdkws.FriendRequest
- Data []map[string]interface{} `json:"data"`
-}
diff --git a/pkg/server_api_params/group_api_struct.go b/pkg/server_api_params/group_api_struct.go
deleted file mode 100644
index 4ff561227..000000000
--- a/pkg/server_api_params/group_api_struct.go
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-import "github.com/openimsdk/protocol/sdkws"
-
-type CommResp struct {
- ErrCode int32 `json:"errCode"`
- ErrMsg string `json:"errMsg"`
- ErrDlt string `json:"errDlt"`
-}
-type CommDataResp struct {
- CommResp
- Data []map[string]interface{} `json:"data"`
-}
-type CommDataRespOne struct {
- CommResp
- Data map[string]interface{} `json:"data"`
-}
-
-type KickGroupMemberReq struct {
- GroupID string `json:"groupID" binding:"required"`
- KickedUserIDList []string `json:"kickedUserIDList" binding:"required"`
- Reason string `json:"reason"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type KickGroupMemberResp struct {
- CommResp
- UserIDResultList []*UserIDResult `json:"data"`
-}
-
-type GetGroupMembersInfoReq struct {
- GroupID string `json:"groupID" binding:"required"`
- MemberList []string `json:"memberList" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type GetGroupMembersInfoResp struct {
- CommResp
- MemberList []*sdkws.GroupMemberFullInfo `json:"-"`
- Data []map[string]interface{} `json:"data"`
-}
-
-type InviteUserToGroupReq struct {
- GroupID string `json:"groupID" binding:"required"`
- InvitedUserIDList []string `json:"invitedUserIDList" binding:"required"`
- Reason string `json:"reason"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type InviteUserToGroupResp struct {
- CommResp
- UserIDResultList []*UserIDResult `json:"data"`
-}
-
-type GetJoinedGroupListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
-}
-type GetJoinedGroupListResp struct {
- CommResp
- GroupInfoList []*sdkws.GroupInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetGroupMemberListReq struct {
- GroupID string `json:"groupID"`
- Filter int32 `json:"filter"`
- NextSeq int32 `json:"nextSeq"`
- OperationID string `json:"operationID"`
-}
-type GetGroupMemberListResp struct {
- CommResp
- NextSeq int32 `json:"nextSeq"`
- MemberList []*sdkws.GroupMemberFullInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetGroupAllMemberReq struct {
- GroupID string `json:"groupID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
- Offset int32 `json:"offset"`
- Count int32 `json:"count"`
-}
-type GetGroupAllMemberResp struct {
- CommResp
- MemberList []*sdkws.GroupMemberFullInfo `json:"-"`
- Data []map[string]interface{} `json:"data"`
-}
-
-type CreateGroupReq struct {
- MemberList []*GroupAddMemberInfo `json:"memberList" binding:"required"`
- OwnerUserID string `json:"ownerUserID" binding:"required"`
- GroupType int32 `json:"groupType"`
- GroupName string `json:"groupName"`
- Notification string `json:"notification"`
- Introduction string `json:"introduction"`
- FaceURL string `json:"faceURL"`
- Ex string `json:"ex"`
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type CreateGroupResp struct {
- CommResp
- GroupInfo sdkws.GroupInfo
- Data map[string]interface{} `json:"data"`
-}
-
-type GetGroupApplicationListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"` //作为管理员或群主收到的 进群申请
-}
-type GetGroupApplicationListResp struct {
- CommResp
- GroupRequestList []*sdkws.GroupRequest
- Data []map[string]interface{} `json:"data"`
-}
-
-type GetUserReqGroupApplicationListReq struct {
- OperationID string `json:"operationID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
-}
-
-type GetUserRespGroupApplicationResp struct {
- CommResp
- GroupRequestList []*sdkws.GroupRequest `json:"-"`
-}
-
-type GetGroupInfoReq struct {
- GroupIDList []string `json:"groupIDList" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type GetGroupInfoResp struct {
- CommResp
- GroupInfoList []*sdkws.GroupInfo `json:"-"`
- Data []map[string]interface{} `json:"data"`
-}
-
-//type GroupInfoAlias struct {
-// GroupID string `protobuf:"bytes,1,opt,name=groupID" json:"groupID,omitempty"`
-// GroupName string `protobuf:"bytes,2,opt,name=groupName" json:"groupName,omitempty"`
-// NotificationCmd string `protobuf:"bytes,3,opt,name=notification" json:"notification,omitempty"`
-// Introduction string `protobuf:"bytes,4,opt,name=introduction" json:"introduction,omitempty"`
-// FaceURL string `protobuf:"bytes,5,opt,name=faceURL" json:"faceURL,omitempty"`
-// OwnerUserID string `protobuf:"bytes,6,opt,name=ownerUserID" json:"ownerUserID,omitempty"`
-// CreateTime uint32 `protobuf:"varint,7,opt,name=createTime" json:"createTime,omitempty"`
-// MemberCount uint32 `protobuf:"varint,8,opt,name=memberCount" json:"memberCount,omitempty"`
-// Ex string `protobuf:"bytes,9,opt,name=ex" json:"ex,omitempty"`
-// Status int32 `protobuf:"varint,10,opt,name=status" json:"status,omitempty"`
-// CreatorUserID string `protobuf:"bytes,11,opt,name=creatorUserID" json:"creatorUserID,omitempty"`
-// GroupType int32 `protobuf:"varint,12,opt,name=groupType" json:"groupType,omitempty"`
-// NeedVerification int32 `protobuf:"bytes,13,opt,name=needVerification" json:"needVerification,omitempty"`
-//}
-//type GroupInfoAlias struct {
-// GroupInfo
-// NeedVerification int32 `protobuf:"bytes,13,opt,name=needVerification" json:"needVerification,omitempty"`
-//}
-
-type ApplicationGroupResponseReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"` //application from FromUserID
- HandledMsg string `json:"handledMsg"`
- HandleResult int32 `json:"handleResult" binding:"required,oneof=-1 1"`
-}
-type ApplicationGroupResponseResp struct {
- CommResp
-}
-
-type JoinGroupReq struct {
- GroupID string `json:"groupID" binding:"required"`
- ReqMessage string `json:"reqMessage"`
- OperationID string `json:"operationID" binding:"required"`
- JoinSource int32 `json:"joinSource"`
- InviterUserID string `json:"inviterUserID"`
-}
-
-type JoinGroupResp struct {
- CommResp
-}
-
-type QuitGroupReq struct {
- GroupID string `json:"groupID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type QuitGroupResp struct {
- CommResp
-}
-
-type SetGroupInfoReq struct {
- GroupID string `json:"groupID" binding:"required"`
- GroupName string `json:"groupName"`
- Notification string `json:"notification"`
- Introduction string `json:"introduction"`
- FaceURL string `json:"faceURL"`
- Ex string `json:"ex"`
- OperationID string `json:"operationID" binding:"required"`
- NeedVerification *int32 `json:"needVerification" binding:"oneof=0 1 2"`
- LookMemberInfo *int32 `json:"lookMemberInfo" binding:"oneof=0 1"`
- ApplyMemberFriend *int32 `json:"applyMemberFriend" binding:"oneof=0 1"`
-}
-
-type SetGroupInfoResp struct {
- CommResp
-}
-
-type TransferGroupOwnerReq struct {
- GroupID string `json:"groupID" binding:"required"`
- OldOwnerUserID string `json:"oldOwnerUserID" binding:"required"`
- NewOwnerUserID string `json:"newOwnerUserID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type TransferGroupOwnerResp struct {
- CommResp
-}
-
-type DismissGroupReq struct {
- GroupID string `json:"groupID" binding:"required"`
- OperationID string `json:"operationID" binding:"required"`
-}
-type DismissGroupResp struct {
- CommResp
-}
-
-type MuteGroupMemberReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
- MutedSeconds uint32 `json:"mutedSeconds" binding:"required"`
-}
-type MuteGroupMemberResp struct {
- CommResp
-}
-
-type CancelMuteGroupMemberReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
-}
-type CancelMuteGroupMemberResp struct {
- CommResp
-}
-
-type MuteGroupReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
-}
-type MuteGroupResp struct {
- CommResp
-}
-
-type CancelMuteGroupReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
-}
-type CancelMuteGroupResp struct {
- CommResp
-}
-
-type SetGroupMemberNicknameReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
- Nickname string `json:"nickname"`
-}
-
-type SetGroupMemberNicknameResp struct {
- CommResp
-}
-
-type SetGroupMemberBaseInfoReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
-}
-
-type SetGroupMemberInfoReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
- Nickname *string `json:"nickname"`
- FaceURL *string `json:"userGroupFaceUrl"`
- RoleLevel *int32 `json:"roleLevel" validate:"gte=1,lte=3"`
- Ex *string `json:"ex"`
-}
-
-type SetGroupMemberRoleLevelReq struct {
- SetGroupMemberBaseInfoReq
- RoleLevel int `json:"roleLevel"`
-}
-
-type SetGroupMemberRoleLevelResp struct {
- CommResp
-}
-
-type GetGroupAbstractInfoReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GroupID string `json:"groupID" binding:"required"`
-}
-
-type GetGroupAbstractInfoResp struct {
- CommResp
- GroupMemberNumber int32 `json:"groupMemberNumber"`
- GroupMemberListHash uint64 `json:"groupMemberListHash"`
-}
diff --git a/pkg/server_api_params/manage_api_struct.go b/pkg/server_api_params/manage_api_struct.go
deleted file mode 100644
index 1a07833a9..000000000
--- a/pkg/server_api_params/manage_api_struct.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type DeleteUsersReq struct {
- OperationID string `json:"operationID" binding:"required"`
- DeleteUserIDList []string `json:"deleteUserIDList" binding:"required"`
-}
-type DeleteUsersResp struct {
- CommResp
- FailedUserIDList []string `json:"data"`
-}
-type GetAllUsersUidReq struct {
- OperationID string `json:"operationID" binding:"required"`
-}
-type GetAllUsersUidResp struct {
- CommResp
- UserIDList []string `json:"data"`
-}
-type GetUsersOnlineStatusReq struct {
- OperationID string `json:"operationID" binding:"required"`
- UserIDList []string `json:"userIDList" binding:"required,lte=200"`
-}
-type GetUsersOnlineStatusResp struct {
- CommResp
- SuccessResult []GetusersonlinestatusrespSuccessresult `json:"data"`
-}
-type AccountCheckReq struct {
- OperationID string `json:"operationID" binding:"required"`
- CheckUserIDList []string `json:"checkUserIDList" binding:"required,lte=100"`
-}
-type AccountCheckResp struct {
- CommResp
- Results []*AccountCheckResp_SingleUserStatus `json:"data"`
-}
-type AccountCheckResp_SingleUserStatus struct {
- UserID string `protobuf:"bytes,1,opt,name=userID" json:"userID,omitempty"`
- AccountStatus string `protobuf:"bytes,2,opt,name=accountStatus" json:"accountStatus,omitempty"`
-}
-
-type GetusersonlinestatusrespSuccessdetail struct {
- Platform string `protobuf:"bytes,1,opt,name=platform" json:"platform,omitempty"`
- Status string `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-type GetusersonlinestatusrespSuccessresult struct {
- UserID string `protobuf:"bytes,1,opt,name=userID" json:"userID,omitempty"`
- Status string `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
- DetailPlatformStatus []*GetusersonlinestatusrespSuccessdetail `protobuf:"bytes,3,rep,name=detailPlatformStatus" json:"detailPlatformStatus,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-type AccountcheckrespSingleuserstatus struct {
- UserID string `protobuf:"bytes,1,opt,name=userID" json:"userID,omitempty"`
- AccountStatus string `protobuf:"bytes,2,opt,name=accountStatus" json:"accountStatus,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
diff --git a/pkg/server_api_params/minio_api_struct.go b/pkg/server_api_params/minio_api_struct.go
deleted file mode 100644
index bd2132bcd..000000000
--- a/pkg/server_api_params/minio_api_struct.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type MinioStorageCredentialReq struct {
- OperationID string `json:"operationID"`
-}
-
-type MinioStorageCredentialResp struct {
- CommResp
- SecretAccessKey string `json:"secretAccessKey"`
- AccessKeyID string `json:"accessKeyID"`
- SessionToken string `json:"sessionToken"`
- SignerType int `json:"signerType"`
- BucketName string `json:"bucketName"`
- StsEndpointURL string `json:"stsEndpointURL"`
- StorageTime int `json:"storageTime"`
-}
diff --git a/pkg/server_api_params/oss_api_struct.go b/pkg/server_api_params/oss_api_struct.go
deleted file mode 100644
index f6c8b570a..000000000
--- a/pkg/server_api_params/oss_api_struct.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type OSSCredentialReq struct {
- OperationID string `json:"operationID"`
- Filename string `json:"filename"`
- FileType string `json:"file_type"`
-}
-
-type OSSCredentialRespData struct {
- Endpoint string `json:"endpoint"`
- AccessKeyId string `json:"access_key_id"`
- AccessKeySecret string `json:"access_key_secret"`
- Token string `json:"token"`
- Bucket string `json:"bucket"`
- FinalHost string `json:"final_host"`
-}
-
-type OSSCredentialResp struct {
- CommResp
- OssData OSSCredentialRespData `json:"-"`
- Data map[string]interface{} `json:"data"`
-}
diff --git a/pkg/server_api_params/public_struct.go b/pkg/server_api_params/public_struct.go
deleted file mode 100644
index 9c046c0bf..000000000
--- a/pkg/server_api_params/public_struct.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
-)
-
-type ApiUserInfo struct {
- UserID string `json:"userID" binding:"required,min=1,max=64"`
- Nickname string `json:"nickname" binding:"omitempty,min=1,max=64"`
- FaceURL string `json:"faceURL" binding:"omitempty,max=1024"`
- GlobalRecvMsgOpt int32 `json:"globalRecvMsgOpt" binding:"omitempty,oneof=0 1 2"`
- Ex string `json:"ex" binding:"omitempty,max=1024"`
-}
-
-type GroupAddMemberInfo struct {
- UserID string `json:"userID" validate:"required"`
- RoleLevel int32 `json:"roleLevel" validate:"required"`
-}
-
-type PublicUser struct {
- UserID string `json:"userID"`
- Nickname string `json:"nickname"`
- FaceURL string `json:"faceURL"`
- Ex string `json:"ex"`
- CreateTime int64 `json:"createTime"`
-}
-
-type FullUserInfo struct {
- PublicInfo *PublicUser `json:"publicInfo"`
- FriendInfo *model_struct.LocalFriend `json:"friendInfo"`
- BlackInfo *model_struct.LocalBlack `json:"blackInfo"`
-}
-
-type FullUserInfoWithCache struct {
- PublicInfo *PublicUser `json:"publicInfo"`
- FriendInfo *model_struct.LocalFriend `json:"friendInfo"`
- BlackInfo *model_struct.LocalBlack `json:"blackInfo"`
- GroupMemberInfo *model_struct.LocalGroupMember `json:"groupMemberInfo"`
-}
-
-//GroupName string `json:"groupName"`
-// Introduction string `json:"introduction"`
-// NotificationCmd string `json:"notification"`
-// FaceUrl string `json:"faceUrl"`
-// OperationID string `json:"operationID" binding:"required"`
-// GroupType int32 `json:"groupType"`
-// Ex string `json:"ex"`
-
-//type GroupInfo struct {
-// GroupID string `json:"groupID"`
-// GroupName string `json:"groupName"`
-// NotificationCmd string `json:"notification"`
-// Introduction string `json:"introduction"`
-// FaceUrl string `json:"faceUrl"`
-// OwnerUserID string `json:"ownerUserID"`
-// Ex string `json:"ex"`
-// GroupType int32 `json:"groupType"`
-//}
-
-//type GroupMemberFullInfo struct {
-// GroupID string `json:"groupID"`
-// UserID string `json:"userID"`
-// RoleLevel int32 `json:"roleLevel"`
-// JoinTime uint64 `json:"joinTime"`
-// Nickname string `json:"nickname"`
-// FaceUrl string `json:"faceUrl"`
-// FriendRemark string `json:"friendRemark"`
-// AppMangerLevel int32 `json:"appMangerLevel"`
-// JoinSource int32 `json:"joinSource"`
-// OperatorUserID string `json:"operatorUserID"`
-// Ex string `json:"ex"`
-//}
-//
-//type PublicUserInfo struct {
-// UserID string `json:"userID"`
-// Nickname string `json:"nickname"`
-// FaceUrl string `json:"faceUrl"`
-// Gender int32 `json:"gender"`
-//}
-//
-//type UserInfo struct {
-// UserID string `json:"userID"`
-// Nickname string `json:"nickname"`
-// FaceUrl string `json:"faceUrl"`
-// Gender int32 `json:"gender"`
-// Mobile string `json:"mobile"`
-// Birth string `json:"birth"`
-// Email string `json:"email"`
-// Ex string `json:"ex"`
-//}
-//
-//type FriendInfo struct {
-// OwnerUserID string `json:"ownerUserID"`
-// Remark string `json:"remark"`
-// CreateTime int64 `json:"createTime"`
-// FriendUser UserInfo `json:"friendUser"`
-// AddSource int32 `json:"addSource"`
-// OperatorUserID string `json:"operatorUserID"`
-// Ex string `json:"ex"`
-//}
-//
-//type BlackInfo struct {
-// OwnerUserID string `json:"ownerUserID"`
-// CreateTime int64 `json:"createTime"`
-// BlackUser PublicUserInfo `json:"friendUser"`
-// AddSource int32 `json:"addSource"`
-// OperatorUserID string `json:"operatorUserID"`
-// Ex string `json:"ex"`
-//}
-//
-//type GroupRequest struct {
-// UserID string `json:"userID"`
-// GroupID string `json:"groupID"`
-// HandleResult string `json:"handleResult"`
-// ReqMsg string `json:"reqMsg"`
-// HandleMsg string `json:"handleMsg"`
-// ReqTime int64 `json:"reqTime"`
-// HandleUserID string `json:"handleUserID"`
-// HandleTime int64 `json:"handleTime"`
-// Ex string `json:"ex"`
-//}
-//
-//type FriendRequest struct {
-// FromUserID string `json:"fromUserID"`
-// ToUserID string `json:"toUserID"`
-// HandleResult int32 `json:"handleResult"`
-// ReqMessage string `json:"reqMessage"`
-// CreateTime int64 `json:"createTime"`
-// HandlerUserID string `json:"handlerUserID"`
-// HandleMsg string `json:"handleMsg"`
-// HandleTime int64 `json:"handleTime"`
-// Ex string `json:"ex"`
-//}
-//
-//
-//
diff --git a/pkg/server_api_params/super_group.go b/pkg/server_api_params/super_group.go
deleted file mode 100644
index 7ba3c91e0..000000000
--- a/pkg/server_api_params/super_group.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type GetJoinedSuperGroupReq struct {
- GetJoinedGroupListReq
-}
-
-type GetJoinedSuperGroupResp struct {
- GetJoinedGroupListResp
-}
-type GetSuperGroupsInfoReq struct {
- GetGroupInfoReq
-}
-
-type GetSuperGroupsInfoResp struct {
- GetGroupInfoResp
-}
diff --git a/pkg/server_api_params/third_api_struct.go b/pkg/server_api_params/third_api_struct.go
deleted file mode 100644
index 9390b0d94..000000000
--- a/pkg/server_api_params/third_api_struct.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-type FcmUpdateTokenReq struct {
- OperationID string `json:"operationID" binding:"required"`
- Platform int `json:"platform" binding:"required,min=1,max=2"` //only for ios + android
- FcmToken string `json:"fcmToken" binding:"required"`
-}
-
-type FcmUpdateTokenResp struct {
- CommResp
-}
-
-type SetAppBadgeReq struct {
- OperationID string `json:"operationID" binding:"required"`
- FromUserID string `json:"fromUserID" binding:"required"`
- AppUnreadCount int32 `json:"appUnreadCount" binding:"required"`
-}
-
-type SetAppBadgeResp struct {
- CommResp
-}
diff --git a/pkg/server_api_params/user_api_struct.go b/pkg/server_api_params/user_api_struct.go
deleted file mode 100644
index 4db647f0e..000000000
--- a/pkg/server_api_params/user_api_struct.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package server_api_params
-
-import (
- "github.com/openimsdk/protocol/sdkws"
-)
-
-type GetUsersInfoReq struct {
- OperationID string `json:"operationID" binding:"required"`
- UserIDList []string `json:"userIDList" binding:"required"`
-}
-type GetUsersInfoResp struct {
- CommResp
- UserInfoList []*sdkws.PublicUserInfo
- Data []map[string]interface{} `json:"data"`
-}
-
-type UpdateSelfUserInfoReq struct {
- ApiUserInfo
- OperationID string `json:"operationID" binding:"required"`
-}
-
-type UpdateUserInfoResp struct {
- CommResp
-}
-type SetGlobalRecvMessageOptReq struct {
- OperationID string `json:"operationID" binding:"required"`
- GlobalRecvMsgOpt *int32 `json:"globalRecvMsgOpt" binding:"omitempty,oneof=0 1 2"`
-}
-type SetGlobalRecvMessageOptResp struct {
- CommResp
-}
-type GetSelfUserInfoReq struct {
- OperationID string `json:"operationID" binding:"required"`
- UserID string `json:"userID" binding:"required"`
-}
-type GetSelfUserInfoResp struct {
- CommResp
- UserInfo *sdkws.UserInfo `json:"-"`
- Data map[string]interface{} `json:"data"`
-}
diff --git a/pkg/syncer/syncer.go b/pkg/syncer/syncer.go
index aad6f821d..7a4e386d8 100644
--- a/pkg/syncer/syncer.go
+++ b/pkg/syncer/syncer.go
@@ -20,7 +20,7 @@ import (
"github.com/google/go-cmp/cmp"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
"github.com/openimsdk/openim-sdk-core/v3/pkg/page"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/utils/datautil"
@@ -84,7 +84,7 @@ type Syncer[T, RESP any, V comparable] struct {
batchPageRespConvertFunc func(resp *RESP) []T
reqApiRouter string
ts string // Represents the type of T as a string.
- fullSyncLimit int
+ fullSyncLimit int64
}
type NoResp struct{}
@@ -197,7 +197,7 @@ func WithReqApiRouter[T, RESP any, V comparable](router string) Option[T, RESP,
}
// WithFullSyncLimit sets the fullSyncLimit for the Syncer.
-func WithFullSyncLimit[T, RESP any, V comparable](limit int) Option[T, RESP, V] {
+func WithFullSyncLimit[T, RESP any, V comparable](limit int64) Option[T, RESP, V] {
return func(s *Syncer[T, RESP, V]) {
s.fullSyncLimit = limit
}
@@ -369,7 +369,7 @@ func (s *Syncer[T, RESP, V]) FullSync(ctx context.Context, entityID string) (err
batchReq := s.batchPageReq(entityID)
// Batch page pull data and insert server data
- if err = util.FetchAndInsertPagedData(ctx, s.reqApiRouter, batchReq, s.batchPageRespConvertFunc,
+ if err = network.FetchAndInsertPagedData(ctx, s.reqApiRouter, batchReq, s.batchPageRespConvertFunc,
s.batchInsert, s.insert, s.fullSyncLimit); err != nil {
return errs.New("full sync batch insert failed", "err", err.Error(), "type", s.ts)
}
diff --git a/internal/incrversion/option.go b/pkg/syncer/version_synchronizer.go
similarity index 81%
rename from internal/incrversion/option.go
rename to pkg/syncer/version_synchronizer.go
index ca2af52f2..b8c317586 100644
--- a/internal/incrversion/option.go
+++ b/pkg/syncer/version_synchronizer.go
@@ -1,9 +1,8 @@
-package incrversion
+package syncer
import (
"context"
"reflect"
- "sort"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
@@ -31,6 +30,7 @@ type VersionSynchronizer[V, R any] struct {
Syncer func(server, local []V) error
FullSyncer func(ctx context.Context) error
FullID func(ctx context.Context) ([]string, error)
+ IDOrderChanged func(resp R) bool
}
func (o *VersionSynchronizer[V, R]) getVersionInfo() (*model_struct.LocalVersionSync, error) {
@@ -53,7 +53,7 @@ func judgeInterfaceIsNil(data any) bool {
return reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil()
}
-func (o *VersionSynchronizer[V, R]) Sync() error {
+func (o *VersionSynchronizer[V, R]) IncrementalSync() error {
var lvs *model_struct.LocalVersionSync
var resp R
var extraData any
@@ -100,7 +100,7 @@ func (o *VersionSynchronizer[V, R]) Sync() error {
}
} else {
if len(delIDs) > 0 {
- lvs.UIDList = DeleteElements(lvs.UIDList, delIDs)
+ lvs.UIDList = datautil.DeleteElems(lvs.UIDList, delIDs...)
}
if len(insert) > 0 {
lvs.UIDList = append(lvs.UIDList, datautil.Slice(insert, o.Key)...)
@@ -134,6 +134,14 @@ func (o *VersionSynchronizer[V, R]) Sync() error {
}
}
+ // The ordering of fullID has changed due to modifications such as group role level changes or friend list reordering.
+ // Therefore, it is necessary to refresh and obtain the fullID again.
+ if o.IDOrderChanged != nil && o.IDOrderChanged(resp) {
+ lvs.UIDList, err = o.FullID(o.Ctx)
+ if err != nil {
+ return err
+ }
+ }
}
return o.updateVersionInfo(lvs, resp)
}
@@ -166,11 +174,11 @@ func (o *VersionSynchronizer[V, R]) CheckVersionSync() error {
if versionID != lvs.VersionID {
log.ZDebug(o.Ctx, "version id not match", errs.New("version id not match"), "versionID", versionID, "localVersionID", lvs.VersionID)
o.ServerVersion = nil
- return o.Sync()
+ return o.IncrementalSync()
}
if lvs.Version+1 == version {
if len(delIDs) > 0 {
- lvs.UIDList = DeleteElements(lvs.UIDList, delIDs)
+ lvs.UIDList = datautil.DeleteElems(lvs.UIDList, delIDs...)
}
if len(insert) > 0 {
lvs.UIDList = append(lvs.UIDList, datautil.Slice(insert, o.Key)...)
@@ -202,6 +210,14 @@ func (o *VersionSynchronizer[V, R]) CheckVersionSync() error {
}
}
+ // The ordering of fullID has changed due to modifications such as group role level changes or friend list reordering.
+ // Therefore, it is necessary to refresh and obtain the fullID again.
+ if o.IDOrderChanged != nil && o.IDOrderChanged(resp) {
+ lvs.UIDList, err = o.FullID(o.Ctx)
+ if err != nil {
+ return err
+ }
+ }
return o.updateVersionInfo(lvs, resp)
} else if version <= lvs.Version {
log.ZWarn(o.Ctx, "version less than local version", errs.New("version less than local version"),
@@ -212,53 +228,6 @@ func (o *VersionSynchronizer[V, R]) CheckVersionSync() error {
//it indicates that some pushed data might be missing.
//Trigger the complete client-server incremental synchronization.
o.ServerVersion = nil
- return o.Sync()
+ return o.IncrementalSync()
}
}
-
-// DeleteElements removes elements from a slice that are contained in another slice, while maintaining the order of the slice
-func DeleteElements[E comparable](es []E, toDelete []E) []E {
- // Store the elements to be deleted in a hash set
- deleteSet := make(map[E]struct{}, len(toDelete))
- for _, e := range toDelete {
- deleteSet[e] = struct{}{}
- }
-
- // Use an index j to track the new slice position
- j := 0
- for _, e := range es {
- if _, found := deleteSet[e]; !found {
- es[j] = e
- j++
- }
- }
- return es[:j]
-}
-
-// DeleteElement removes a specified element from a slice while maintaining the order of the slice
-func DeleteElement[E comparable](es []E, element E) []E {
- j := 0
- for _, e := range es {
- if e != element {
- es[j] = e
- j++
- }
- }
- return es[:j]
-}
-
-// Slice converts slice types in batches and sorts the resulting slice using a custom comparator
-func Slice[E any, T any](es []E, fn func(e E) T, less func(a, b T) bool) []T {
- // Convert the slice
- v := make([]T, len(es))
- for i := 0; i < len(es); i++ {
- v[i] = fn(es[i])
- }
-
- // Sort the slice
- sort.Slice(v, func(i, j int) bool {
- return less(v[i], v[j])
- })
-
- return v
-}
diff --git a/pkg/utils/aes.go b/pkg/utils/aes.go
deleted file mode 100644
index 244b42463..000000000
--- a/pkg/utils/aes.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package utils
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
-)
-
-func PKCS5Padding(plaintext []byte, blockSize int) []byte {
- padding := blockSize - len(plaintext)%blockSize
- padtext := bytes.Repeat([]byte{byte(padding)}, padding)
- return append(plaintext, padtext...)
-}
-
-func PKCS5UnPadding(origData []byte) []byte {
- length := len(origData)
- unpadding := int(origData[length-1])
- return origData[:(length - unpadding)]
-}
-
-func AesEncrypt(origData, key []byte) ([]byte, error) {
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
-
- blockSize := block.BlockSize()
- origData = PKCS5Padding(origData, blockSize)
- blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
- crypted := make([]byte, len(origData))
- blockMode.CryptBlocks(crypted, origData)
- return crypted, nil
-}
-
-func AesDecrypt(crypted, key []byte) ([]byte, error) {
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
-
- blockSize := block.BlockSize()
- blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
- origData := make([]byte, len(crypted))
- blockMode.CryptBlocks(origData, crypted)
- origData = PKCS5UnPadding(origData)
- return origData, nil
-}
diff --git a/pkg/utils/file.go b/pkg/utils/file.go
index 1af258b0b..dce2ad16e 100644
--- a/pkg/utils/file.go
+++ b/pkg/utils/file.go
@@ -43,10 +43,6 @@ func CopyFile(srcName string, dstName string) (written int64, err error) {
func FileTmpPath(fullPath, dbPrefix string) string {
suffix := path.Ext(fullPath)
- if len(suffix) == 0 {
- sdkLog("suffix err:")
- }
-
return dbPrefix + Md5(fullPath) + suffix //a->b
}
diff --git a/pkg/utils/lock_pool.go b/pkg/utils/lock_pool.go
new file mode 100644
index 000000000..187cc1996
--- /dev/null
+++ b/pkg/utils/lock_pool.go
@@ -0,0 +1,51 @@
+package utils
+
+import "sync"
+
+// resource manager
+type LockPool struct {
+ maxLocks int
+ locks map[string]struct{}
+
+ globalMutex sync.Mutex
+ cond *sync.Cond
+}
+
+// Usage
+
+func NewLockPool(maxLocks int) *LockPool {
+ lp := &LockPool{
+ maxLocks: maxLocks,
+ locks: make(map[string]struct{}),
+ }
+
+ lp.cond = sync.NewCond(&lp.globalMutex)
+ return lp
+}
+
+func (lp *LockPool) Lock(key string) {
+ lp.globalMutex.Lock()
+ defer lp.globalMutex.Unlock()
+
+ for {
+ // If the key exists in the map, then wait.
+ if _, exists := lp.locks[key]; exists {
+ lp.cond.Wait()
+ } else if len(lp.locks) >= lp.maxLocks {
+ // If the number of locks reaches the maximum value, then wait.
+ lp.cond.Wait()
+ } else {
+ lp.locks[key] = struct{}{}
+ return
+ }
+ }
+}
+
+func (lp *LockPool) Unlock(key string) {
+ lp.globalMutex.Lock()
+ defer lp.globalMutex.Unlock()
+
+ delete(lp.locks, key)
+ // wakes all goroutines waiting
+ lp.cond.Broadcast()
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index 978ae54e6..23503e203 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -18,12 +18,10 @@ import (
"crypto/md5"
"encoding/hex"
"encoding/json"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"sort"
- "github.com/golang/protobuf/jsonpb"
- "github.com/golang/protobuf/proto"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"reflect"
@@ -50,22 +48,11 @@ func Md5(s string) string {
return hex.EncodeToString(cipher)
}
-//Get the current timestamp by Second
-
-func GetCurrentTimestampBySecond() int64 {
- return time.Now().Unix()
-}
-
// Get the current timestamp by Mill
func GetCurrentTimestampByMill() int64 {
return time.Now().UnixNano() / 1e6
}
-// Convert nano timestamp to time.Time type
-func UnixNanoSecondToTime(nanoSecond int64) time.Time {
- return time.Unix(0, nanoSecond)
-}
-
// Get the current timestamp by Nano
func GetCurrentTimestampByNano() int64 {
return time.Now().UnixNano()
@@ -106,127 +93,9 @@ func UnixSecondToTime(second int64) time.Time {
func IntToString(i int) string {
return strconv.FormatInt(int64(i), 10)
}
-func Int32ToString(i int32) string {
- return strconv.FormatInt(int64(i), 10)
-}
func Int64ToString(i int64) string {
return strconv.FormatInt(i, 10)
}
-func StringToInt64(i string) int64 {
- j, _ := strconv.ParseInt(i, 10, 64)
- return j
-}
-
-func StringToInt(i string) int {
- j, _ := strconv.Atoi(i)
- return j
-}
-
-func RunFuncName() string {
- pc, _, _, _ := runtime.Caller(2)
- return CleanUpfuncName(runtime.FuncForPC(pc).Name())
-}
-
-func LogBegin(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //pc, b, c, _ := runtime.Caller(1)
- //fname := runtime.FuncForPC(pc).Name()
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println(" [", b[i+1:len(b)], ":", c, "]", cleanUpfuncName(fname), "call funcation begin, args: ", v)
- //}
-}
-
-func LogEnd(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //pc, b, c, _ := runtime.Caller(1)
- //fname := runtime.FuncForPC(pc).Name()
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println(" [", b[i+1:len(b)], ":", c, "]", cleanUpfuncName(fname), "call funcation end, args: ", v)
- //}
-}
-
-func LogStart(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //pc, b, c, _ := runtime.Caller(1)
- //fname := runtime.FuncForPC(pc).Name()
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println(" [", b[i+1:len(b)], ":", c, "]", cleanUpfuncName(fname), "funcation start, args: ", v)
- //}
-}
-
-func LogFReturn(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //pc, b, c, _ := runtime.Caller(1)
- //fname := runtime.FuncForPC(pc).Name()
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println("[", b[i+1:len(b)], ":", c, "]", cleanUpfuncName(fname), "failed return args(info): ", v)
- //}
-}
-
-func LogSReturn(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //pc, b, c, _ := runtime.Caller(1)
- //fname := runtime.FuncForPC(pc).Name()
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println("[", b[i+1:len(b)], ":", c, "]", cleanUpfuncName(fname), "success return args(info): ", v)
- //}
-
-}
-
-func sdkLog(v ...interface{}) {
- //if constant.SdkLogFlag == 1 {
- // return
- //}
- //if open_im_sdk.logger != nil {
- // log2.NewInfo("", v...)
- // return
- //}
- //_, b, c, _ := runtime.Caller(1)
- //i := strings.LastIndex(b, "/")
- //if i != -1 {
- // sLog.Println("[", b[i+1:len(b)], ":", c, "]", v)
- //}
-
-}
-
-type LogInfo struct {
- Info string `json:"info"`
-}
// judge a string whether in the string list
func IsContain(target string, List []string) bool {
@@ -251,17 +120,7 @@ func IsContainInt(target int, List []int) bool {
return false
}
-func IsContainUInt32(target uint32, List []uint32) bool {
-
- for _, element := range List {
-
- if target == element {
- return true
- }
- }
- return false
-}
func GetSwitchFromOptions(Options map[string]bool, key string) (result bool) {
if flag, ok := Options[key]; !ok || flag {
return true
@@ -275,22 +134,6 @@ func SetSwitchFromOptions(Options map[string]bool, key string, value bool) {
func Wrap(err error, message string) error {
return errors.Wrap(err, "==> "+printCallerNameAndLine()+message)
}
-func Unwrap(err error) error {
- for err != nil {
- unwrap, ok := err.(interface {
- Unwrap() error
- })
- if !ok {
- break
- }
- err = unwrap.Unwrap()
- }
- return err
-}
-
-func WithMessage(err error, message string) error {
- return errors.WithMessage(err, "==> "+printCallerNameAndLine()+message)
-}
func GetSelfFuncName() string {
pc, _, _, _ := runtime.Caller(1)
@@ -308,35 +151,16 @@ func printCallerNameAndLine() string {
pc, _, line, _ := runtime.Caller(2)
return runtime.FuncForPC(pc).Name() + "()@" + strconv.Itoa(line) + ": "
}
-func StructToMap(user interface{}) map[string]interface{} {
- data, _ := json.Marshal(user)
- m := make(map[string]interface{})
- json.Unmarshal(data, &m)
- return m
-}
-// funcation GetConversationIDBySessionType(sourceID string, sessionType int) string {
-// switch sessionType {
-// case constant.SingleChatType:
-// return "single_" + sourceID
-// case constant.GroupChatType:
-// return "group_" + sourceID
-// case constant.SuperGroupChatType:
-// return "super_group_" + sourceID
-// case constant.NotificationChatType:
-// return "notification_" + sourceID
-// }
-// return ""
-// }
func GetConversationIDByMsg(msg *sdk_struct.MsgStruct) string {
switch msg.SessionType {
case constant.SingleChatType:
l := []string{msg.SendID, msg.RecvID}
sort.Strings(l)
return "si_" + strings.Join(l, "_") // single chat
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "g_" + msg.GroupID // group chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "sg_" + msg.GroupID // super group chat
case constant.NotificationChatType:
return "sn_" + msg.SendID + "_" + msg.RecvID // server notification chat
@@ -358,36 +182,6 @@ func GetErrTableName(conversationID string) string {
return constant.SuperGroupErrChatLogsTableNamePre + conversationID
}
-type Comparable interface {
- ~int | ~string | ~float64 | ~int32
-}
-
-func RemoveRepeatedElementsInList[T Comparable](slc []T) []T {
- var result []T
- tempMap := map[T]struct{}{}
- for _, e := range slc {
- if _, found := tempMap[e]; !found {
- tempMap[e] = struct{}{}
- result = append(result, e)
- }
- }
-
- return result
-}
-func RemoveOneInList[T comparable](slice []T, val T) []T {
- for i, v := range slice {
- if v == val {
- return append(slice[:i], slice[i+1:]...)
- }
- }
- return slice
-}
-
-/*
-*
-KMP
-*
-*/
func KMP(rMainString string, rSubString string) (isInMainString bool) {
mainString := strings.ToLower(rMainString)
subString := strings.ToLower(rSubString)
@@ -452,22 +246,6 @@ func TrimStringList(list []string) (result []string) {
}
-// Get the intersection of two slices
-func Intersect(slice1, slice2 []int64) []int64 {
- m := make(map[int64]bool)
- n := make([]int64, 0)
- for _, v := range slice1 {
- m[v] = true
- }
- for _, v := range slice2 {
- flag, _ := m[v]
- if flag {
- n = append(n, v)
- }
- }
- return n
-}
-
// Get the diff of two slices
func DifferenceSubset(mainSlice, subSlice []int64) []int64 {
m := make(map[int64]bool)
@@ -482,64 +260,10 @@ func DifferenceSubset(mainSlice, subSlice []int64) []int64 {
}
return n
}
-func DifferenceSubsetString(mainSlice, subSlice []string) []string {
- m := make(map[string]bool)
- n := make([]string, 0)
- for _, v := range subSlice {
- m[v] = true
- }
- for _, v := range mainSlice {
- if !m[v] {
- n = append(n, v)
- }
- }
- return n
-}
-func JsonDataOne(pb proto.Message) map[string]interface{} {
- return ProtoToMap(pb, false)
-}
-
-func ProtoToMap(pb proto.Message, idFix bool) map[string]interface{} {
- marshaler := jsonpb.Marshaler{
- OrigName: true,
- EnumsAsInts: false,
- EmitDefaults: true,
- }
-
- s, _ := marshaler.MarshalToString(pb)
- out := make(map[string]interface{})
- json.Unmarshal([]byte(s), &out)
- if idFix {
- if _, ok := out["id"]; ok {
- out["_id"] = out["id"]
- delete(out, "id")
- }
- }
- return out
-}
-func GetUserIDForMinSeq(userID string) string {
- return "u_" + userID
-}
-
-func GetGroupIDForMinSeq(groupID string) string {
- return "g_" + groupID
-}
-
-func TimeStringToTime(timeString string) (time.Time, error) {
- t, err := time.Parse("2006-01-02", timeString)
- return t, err
-}
func TimeToString(t time.Time) string {
return t.Format("2006-01-02")
}
-func Uint32ListConvert(list []uint32) []int64 {
- var result []int64
- for _, v := range list {
- result = append(result, int64(v))
- }
- return result
-}
func UnmarshalNotificationElem(bytes []byte, t interface{}) error {
var n sdk_struct.NotificationElem
diff --git a/scripts/LICENSE/LICENSE b/scripts/LICENSE/LICENSE
deleted file mode 100644
index 261eeb9e9..000000000
--- a/scripts/LICENSE/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/scripts/LICENSE/LICENSE_TEMPLATES b/scripts/LICENSE/LICENSE_TEMPLATES
deleted file mode 100644
index dbc5ce2c8..000000000
--- a/scripts/LICENSE/LICENSE_TEMPLATES
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright © {{.Year}} {{.Holder}} All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/scripts/githooks/commit-msg b/scripts/githooks/commit-msg
deleted file mode 100644
index 99f4067b9..000000000
--- a/scripts/githooks/commit-msg
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env bash
-# Copyright © 2023 OpenIMSDK.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ==============================================================================
-#
-# Store this file as .git/hooks/commit-msg in your repository in order to
-# enforce checking for proper commit message format before actual commits.
-# You may need to make the scripts executable by 'chmod +x .git/hooks/commit-msg'.
-
-# commit-msg use go-gitlint tool, install go-gitlint via `go get github.com/llorllale/go-gitlint/cmd/go-gitlint`
-# go-gitlint --msg-file="$1"
-
-# An example hook scripts to check the commit log message.
-# Called by "git commit" with one argument, the name of the file
-# that has the commit message. The hook should exit with non-zero
-# status after issuing an appropriate message if it wants to stop the
-# commit. The hook is allowed to edit the commit message file.
-
-YELLOW="\e[93m"
-GREEN="\e[32m"
-RED="\e[31m"
-ENDCOLOR="\e[0m"
-
-printMessage() {
- printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printSuccess() {
- printf "${GREEN}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printError() {
- printf "${RED}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printMessage "Running the OpenIM commit-msg hook."
-
-# This example catches duplicate Signed-off-by lines.
-
-test "" = "$(grep '^Signed-off-by: ' "$1" |
- sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
- echo >&2 Duplicate Signed-off-by lines.
- exit 1
-}
-
-# TODO: go-gitlint dir set
-OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
-GITLINT_DIR="$OPENIM_ROOT/_output/tools/go-gitlint"
-
-$GITLINT_DIR \
- --msg-file=$1 \
- --subject-regex="^(build|chore|ci|docs|feat|feature|fix|perf|refactor|revert|style|test)(.*)?:\s?.*" \
- --subject-maxlen=150 \
- --subject-minlen=10 \
- --body-regex=".*" \
- --max-parents=1
-
-if [ $? -ne 0 ]
-then
- if ! command -v $GITLINT_DIR &>/dev/null; then
- printError "$GITLINT_DIR not found. Please run 'make tools' OR 'make tools.verify.go-gitlint' make verto install it."
- fi
- printError "Please fix your commit message to match kubecub coding standards"
- printError "https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md"
- exit 1
-fi
-
-### Add Sign-off-by line to the end of the commit message
-# Get local git config
-NAME=$(git config user.name)
-EMAIL=$(git config user.email)
-
-# Check if the commit message contains a sign-off line
-grep -qs "^Signed-off-by: " "$1"
-SIGNED_OFF_BY_EXISTS=$?
-
-# Add "Signed-off-by" line if it doesn't exist
-if [ $SIGNED_OFF_BY_EXISTS -ne 0 ]; then
- echo -e "\nSigned-off-by: $NAME <$EMAIL>" >> "$1"
-fi
\ No newline at end of file
diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit
deleted file mode 100644
index cad60e163..000000000
--- a/scripts/githooks/pre-commit
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env bash
-# Copyright © 2023 OpenIMSDK.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ==============================================================================
-# This is a pre-commit hook that ensures attempts to commit files that are
-# are larger than $limit to your _local_ repo fail, with a helpful error message.
-
-# You can override the default limit of 2MB by supplying the environment variable:
-# GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB"
-#
-# ==============================================================================
-#
-
-LC_ALL=C
-
-local_branch="$(git rev-parse --abbrev-ref HEAD)"
-valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$"
-
-YELLOW="\e[93m"
-GREEN="\e[32m"
-RED="\e[31m"
-ENDCOLOR="\e[0m"
-
-printMessage() {
- printf "${YELLOW}openim : $1${ENDCOLOR}\n"
-}
-
-printSuccess() {
- printf "${GREEN}openim : $1${ENDCOLOR}\n"
-}
-
-printError() {
- printf "${RED}openim : $1${ENDCOLOR}\n"
-}
-
-printMessage "Running local openim pre-commit hook."
-
-# flutter format .
-# https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md
-# TODO! GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB"
-# Maximum file size limit in bytes
-limit=${GIT_FILE_SIZE_LIMIT:-2000000} # Default 2MB
-limitInMB=$(( $limit / 1000000 ))
-
-function file_too_large(){
- filename=$0
- filesize=$(( $1 / 2**20 ))
-
- cat < /dev/null 2>&1
-then
- against=HEAD
-else
- against="$empty_tree"
-fi
-
-# Set split so that for loop below can handle spaces in file names by splitting on line breaks
-IFS='
-'
-
-shouldFail=false
-for file in $( git diff-index --cached --name-only $against ); do
- file_size=$(([ ! -f $file ] && echo 0) || (ls -la $file | awk '{ print $5 }'))
- if [ "$file_size" -gt "$limit" ]; then
- printError "File $file is $(( $file_size / 10**6 )) MB, which is larger than our configured limit of $limitInMB MB"
- shouldFail=true
- fi
-done
-
-if $shouldFail
-then
- printMessage "If you really need to commit this file, you can override the size limit by setting the GIT_FILE_SIZE_LIMIT environment variable, e.g. GIT_FILE_SIZE_LIMIT=42000000 for 42MB. Or, commit with the --no-verify switch to skip the check entirely."
- printError "Commit aborted"
- exit 1;
-fi
-
-if [[ ! $local_branch =~ $valid_branch_regex ]]
-then
- printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex.
-Your commit will be rejected. You should rename your branch to a valid name(feat/name OR bug/name) and try again."
- printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694"
- exit 1
-fi
\ No newline at end of file
diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push
deleted file mode 100644
index 2985313b7..000000000
--- a/scripts/githooks/pre-push
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env bash
-# Copyright © 2023 OpenIMSDK.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ==============================================================================
-#
-
-YELLOW="\e[93m"
-GREEN="\e[32m"
-RED="\e[31m"
-ENDCOLOR="\e[0m"
-
-local_branch="$(git rev-parse --abbrev-ref HEAD)"
-valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$"
-
-printMessage() {
- printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printSuccess() {
- printf "${GREEN}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printError() {
- printf "${RED}OpenIM : $1${ENDCOLOR}\n"
-}
-
-printMessage "Running local OpenIM pre-push hook."
-
-if [[ `git status --porcelain` ]]; then
- printError "This scripts needs to run against committed code only. Please commit or stash you changes."
- exit 1
-fi
-
-COLOR_SUFFIX="\033[0m"
-
-BLACK_PREFIX="\033[30m"
-RED_PREFIX="\033[31m"
-GREEN_PREFIX="\033[32m"
-BACKGROUND_GREEN="\033[33m"
-BLUE_PREFIX="\033[34m"
-PURPLE_PREFIX="\033[35m"
-SKY_BLUE_PREFIX="\033[36m"
-WHITE_PREFIX="\033[37m"
-BOLD_PREFIX="\033[1m"
-UNDERLINE_PREFIX="\033[4m"
-ITALIC_PREFIX="\033[3m"
-
-# Function to print colored text
-print_color() {
- local text=$1
- local color=$2
- echo -e "${color}${text}${COLOR_SUFFIX}"
-}
-
-# Function to print section separator
-print_separator() {
- print_color "==========================================================" ${PURPLE_PREFIX}
-}
-
-# Get current time
-time=$(date +"%Y-%m-%d %H:%M:%S")
-
-# Print section separator
-print_separator
-
-# Print time of submission
-print_color "PTIME: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}"
-echo ""
-author=$(git config user.name)
-repository=$(basename -s .git $(git config --get remote.origin.url))
-
-# Print additional information if needed
-print_color "Repository: ${repository}" "${BLUE_PREFIX}"
-echo ""
-
-print_color "Author: ${author}" "${PURPLE_PREFIX}"
-
-# Print section separator
-print_separator
-
-file_list=$(git diff --name-status HEAD @{u})
-added_files=$(grep -c '^A' <<< "$file_list")
-modified_files=$(grep -c '^M' <<< "$file_list")
-deleted_files=$(grep -c '^D' <<< "$file_list")
-
-print_color "Added Files: ${added_files}" "${BACKGROUND_GREEN}"
-print_color "Modified Files: ${modified_files}" "${BACKGROUND_GREEN}"
-print_color "Deleted Files: ${deleted_files}" "${BACKGROUND_GREEN}"
-
-if [[ ! $local_branch =~ $valid_branch_regex ]]
-then
- printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex.
-Your commit will be rejected. You should rename your branch to a valid name(feat/name OR bug/name) and try again."
- printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694"
- exit 1
-fi
-
-#
-#printMessage "Running the Flutter analyzer"
-#flutter analyze
-#
-#if [ $? -ne 0 ]; then
-# printError "Flutter analyzer error"
-# exit 1
-#fi
-#
-#printMessage "Finished running the Flutter analyzer"
diff --git a/scripts/template/footer.md.tmpl b/scripts/template/footer.md.tmpl
index 75a42d23c..a0b84766c 100644
--- a/scripts/template/footer.md.tmpl
+++ b/scripts/template/footer.md.tmpl
@@ -8,7 +8,7 @@ If you encounter any problems during its usage, please create an issue in the [G
**Here are some ways to get involved with the OpenIM community:**
-📢 **Slack Channel**: Join our Slack channels for discussions, communication, and support. Click [here](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) to join the Open-IM-Server Slack team channel.
+📢 **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.
📧 **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).
diff --git a/sdk_struct/sdk_struct.go b/sdk_struct/sdk_struct.go
index 3698854d7..22a7cf7ff 100644
--- a/sdk_struct/sdk_struct.go
+++ b/sdk_struct/sdk_struct.go
@@ -201,46 +201,52 @@ type TypingElem struct {
MsgTips string `json:"msgTips,omitempty"`
}
+type StreamElem struct {
+ Type string `json:"type,omitempty"`
+ Content string `json:"content,omitempty"`
+ Packets []string `json:"packets,omitempty"`
+ End bool `json:"end"`
+}
+
type MsgStruct struct {
- ClientMsgID string `json:"clientMsgID,omitempty"`
- ServerMsgID string `json:"serverMsgID,omitempty"`
- CreateTime int64 `json:"createTime"`
- SendTime int64 `json:"sendTime"`
- SessionType int32 `json:"sessionType"`
- SendID string `json:"sendID,omitempty"`
- RecvID string `json:"recvID,omitempty"`
- MsgFrom int32 `json:"msgFrom"`
- ContentType int32 `json:"contentType"`
- SenderPlatformID int32 `json:"senderPlatformID"`
- SenderNickname string `json:"senderNickname,omitempty"`
- SenderFaceURL string `json:"senderFaceUrl,omitempty"`
- GroupID string `json:"groupID,omitempty"`
- Content string `json:"content,omitempty"`
- Seq int64 `json:"seq"`
- IsRead bool `json:"isRead"`
- Status int32 `json:"status"`
- IsReact bool `json:"isReact,omitempty"`
- IsExternalExtensions bool `json:"isExternalExtensions,omitempty"`
- OfflinePush *sdkws.OfflinePushInfo `json:"offlinePush,omitempty"`
- AttachedInfo string `json:"attachedInfo,omitempty"`
- Ex string `json:"ex,omitempty"`
- LocalEx string `json:"localEx,omitempty"`
- TextElem *TextElem `json:"textElem,omitempty"`
- CardElem *CardElem `json:"cardElem,omitempty"`
- PictureElem *PictureElem `json:"pictureElem,omitempty"`
- SoundElem *SoundElem `json:"soundElem,omitempty"`
- VideoElem *VideoElem `json:"videoElem,omitempty"`
- FileElem *FileElem `json:"fileElem,omitempty"`
- MergeElem *MergeElem `json:"mergeElem,omitempty"`
- AtTextElem *AtTextElem `json:"atTextElem,omitempty"`
- FaceElem *FaceElem `json:"faceElem,omitempty"`
- LocationElem *LocationElem `json:"locationElem,omitempty"`
- CustomElem *CustomElem `json:"customElem,omitempty"`
- QuoteElem *QuoteElem `json:"quoteElem,omitempty"`
- NotificationElem *NotificationElem `json:"notificationElem,omitempty"`
- AdvancedTextElem *AdvancedTextElem `json:"advancedTextElem,omitempty"`
- TypingElem *TypingElem `json:"typingElem,omitempty"`
- AttachedInfoElem *AttachedInfoElem `json:"attachedInfoElem,omitempty"`
+ ClientMsgID string `json:"clientMsgID,omitempty"`
+ ServerMsgID string `json:"serverMsgID,omitempty"`
+ CreateTime int64 `json:"createTime"`
+ SendTime int64 `json:"sendTime"`
+ SessionType int32 `json:"sessionType"`
+ SendID string `json:"sendID,omitempty"`
+ RecvID string `json:"recvID,omitempty"`
+ MsgFrom int32 `json:"msgFrom"`
+ ContentType int32 `json:"contentType"`
+ SenderPlatformID int32 `json:"senderPlatformID"`
+ SenderNickname string `json:"senderNickname,omitempty"`
+ SenderFaceURL string `json:"senderFaceUrl,omitempty"`
+ GroupID string `json:"groupID,omitempty"`
+ Content string `json:"content,omitempty"`
+ Seq int64 `json:"seq"`
+ IsRead bool `json:"isRead"`
+ Status int32 `json:"status"`
+ OfflinePush *sdkws.OfflinePushInfo `json:"offlinePush,omitempty"`
+ AttachedInfo string `json:"attachedInfo,omitempty"`
+ Ex string `json:"ex,omitempty"`
+ LocalEx string `json:"localEx,omitempty"`
+ TextElem *TextElem `json:"textElem,omitempty"`
+ CardElem *CardElem `json:"cardElem,omitempty"`
+ PictureElem *PictureElem `json:"pictureElem,omitempty"`
+ SoundElem *SoundElem `json:"soundElem,omitempty"`
+ VideoElem *VideoElem `json:"videoElem,omitempty"`
+ FileElem *FileElem `json:"fileElem,omitempty"`
+ MergeElem *MergeElem `json:"mergeElem,omitempty"`
+ AtTextElem *AtTextElem `json:"atTextElem,omitempty"`
+ FaceElem *FaceElem `json:"faceElem,omitempty"`
+ LocationElem *LocationElem `json:"locationElem,omitempty"`
+ CustomElem *CustomElem `json:"customElem,omitempty"`
+ QuoteElem *QuoteElem `json:"quoteElem,omitempty"`
+ NotificationElem *NotificationElem `json:"notificationElem,omitempty"`
+ AdvancedTextElem *AdvancedTextElem `json:"advancedTextElem,omitempty"`
+ TypingElem *TypingElem `json:"typingElem,omitempty"`
+ StreamElem *StreamElem `json:"streamElem,omitempty"`
+ AttachedInfoElem *AttachedInfoElem `json:"attachedInfoElem,omitempty"`
}
type AtInfo struct {
@@ -324,43 +330,20 @@ type CmdNewMsgComeToConversation struct {
SyncFlag int
}
-type CmdPushMsgToMsgSync struct {
- Msgs []*sdkws.PushMessages
-}
-
-type CmdMaxSeqToMsgSync struct {
- ConversationMaxSeqOnSvr map[string]int64
+type CmdMsgSyncInReinstall struct {
+ Msgs map[string]*sdkws.PullMsgs
+ Total int
}
-type CmdJoinedSuperGroup struct {
- OperationID string
+type BasicInfo struct {
+ Nickname string
+ FaceURL string
}
-type OANotificationElem struct {
- NotificationName string `mapstructure:"notificationName" validate:"required"`
- NotificationFaceURL string `mapstructure:"notificationFaceURL" validate:"required"`
- NotificationType int32 `mapstructure:"notificationType" validate:"required"`
- Text string `mapstructure:"text" validate:"required"`
- Url string `mapstructure:"url"`
- MixType int32 `mapstructure:"mixType"`
- Image struct {
- SourceUrl string `mapstructure:"sourceURL"`
- SnapshotUrl string `mapstructure:"snapshotURL"`
- } `mapstructure:"image"`
- Video struct {
- SourceUrl string `mapstructure:"sourceURL"`
- SnapshotUrl string `mapstructure:"snapshotURL"`
- Duration int64 `mapstructure:"duration"`
- } `mapstructure:"video"`
- File struct {
- SourceUrl string `mapstructure:"sourceURL"`
- FileName string `mapstructure:"fileName"`
- FileSize int64 `mapstructure:"fileSize"`
- } `mapstructure:"file"`
- Ex string `mapstructure:"ex"`
-}
-type MsgDeleteNotificationElem struct {
- GroupID string `json:"groupID"`
- IsAllDelete bool `json:"isAllDelete"`
- SeqList []string `json:"seqList"`
+type PublicUser struct {
+ UserID string `json:"userID"`
+ Nickname string `json:"nickname"`
+ FaceURL string `json:"faceURL"`
+ Ex string `json:"ex"`
+ CreateTime int64 `json:"createTime"`
}
diff --git a/test/Makefile b/test/Makefile
deleted file mode 100644
index f45510b51..000000000
--- a/test/Makefile
+++ /dev/null
@@ -1,27 +0,0 @@
-.PHONY: all build run gotool install clean help
-
-BINARY_NAME=main
-BIN_DIR=./
-LAN_FILE=.go
-GO_FILE:=${BINARY_NAME}${LAN_FILE}
-
-all: gotool build
-
-build:
- CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o ${BINARY_NAME} ${GO_FILE}
-
-run:
- @go run ./
-
-gotool:
- go fmt ./
- go vet ./
-
-install:
- make build
- mv main open_im_test_client
-
-clean:
- @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi
-
-
diff --git a/test/account.go b/test/account.go
deleted file mode 100644
index fd969d778..000000000
--- a/test/account.go
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "context"
- "errors"
- "github.com/openimsdk/openim-sdk-core/v3/internal/util"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "net"
- "os"
- "strconv"
- "sync"
- "time"
-
- authPB "github.com/openimsdk/protocol/auth"
- "github.com/openimsdk/protocol/sdkws"
- userPB "github.com/openimsdk/protocol/user"
- "github.com/openimsdk/tools/log"
-)
-
-func GenUid(uid int, prefix string) string {
- if getMyIP() == "" {
- log.ZError(ctx, "getMyIP() failed, exit ", errors.New("getMyIP() failed"))
- os.Exit(1)
- }
- UidPrefix := getMyIP() + "_" + prefix + "_"
- return UidPrefix + strconv.FormatInt(int64(uid), 10)
-}
-
-func RegisterOnlineAccounts(number int) {
- var wg sync.WaitGroup
- wg.Add(number)
- for i := 0; i < number; i++ {
- go func(t int) {
- userID := GenUid(t, "online")
- register(userID)
- log.ZInfo(ctx, "register ", userID)
- wg.Done()
- }(i)
-
- }
- wg.Wait()
- log.ZInfo(ctx, "RegisterAccounts finish ", number)
-}
-
-type GetTokenReq struct {
- Secret string `json:"secret"`
- Platform int `json:"platform"`
- Uid string `json:"uid"`
-}
-
-type RegisterReq struct {
- Secret string `json:"secret"`
- Platform int `json:"platform"`
- Uid string `json:"uid"`
- Name string `json:"name"`
-}
-
-type ResToken struct {
- Data struct {
- ExpiredTime int64 `json:"expiredTime"`
- Token string `json:"token"`
- Uid string `json:"uid"`
- }
- ErrCode int `json:"errCode"`
- ErrMsg string `json:"errMsg"`
-}
-
-var AdminToken = ""
-
-func init() {
- AdminToken = getToken("openIM123456")
- if err := log.InitFromConfig("open-im-sdk-core", "", int(LogLevel), IsLogStandardOutput, false, LogFilePath, 0, 24, "23432"); err != nil {
- //fmt.Println("123456", "log init failed ", err.Error())
- }
-
-}
-
-var ctx context.Context
-
-func register(uid string) error {
- ctx = ccontext.WithInfo(context.Background(), &ccontext.GlobalConfig{
- UserID: uid,
- Token: AdminToken,
- IMConfig: sdk_struct.IMConfig{
- PlatformID: PlatformID,
- ApiAddr: APIADDR,
- WsAddr: WSADDR,
- LogLevel: LogLevel,
- },
- })
- ctx = ccontext.WithOperationID(ctx, "123456")
-
- //ACCOUNTCHECK
- var getAccountCheckReq userPB.AccountCheckReq
- var getAccountCheckResp userPB.AccountCheckResp
- getAccountCheckReq.CheckUserIDs = []string{uid}
-
- for {
- err := util.ApiPost(ctx, "/user/account_check", &getAccountCheckReq, &getAccountCheckResp)
- if err != nil {
- return err
- }
- if len(getAccountCheckResp.Results) == 1 &&
- getAccountCheckResp.Results[0].AccountStatus == "registered" {
- log.ZWarn(ctx, "account already registered", errors.New("Already registered "), "userIDs", getAccountCheckReq.CheckUserIDs[0],
- "uid", uid, "getAccountCheckResp", getAccountCheckResp)
- userLock.Lock()
- allUserID = append(allUserID, uid)
- userLock.Unlock()
- return nil
- } else if len(getAccountCheckResp.Results) == 1 &&
- getAccountCheckResp.Results[0].AccountStatus == "unregistered" {
- log.ZInfo(ctx, "account not register", "userIDs", getAccountCheckReq.CheckUserIDs[0], "uid", uid, "getAccountCheckResp",
- getAccountCheckResp)
- break
- } else {
- log.ZError(ctx, " failed, continue ", err, "userIDs", getAccountCheckReq.CheckUserIDs[0], "register address",
- REGISTERADDR, "getAccountCheckReq", getAccountCheckReq)
- continue
- }
- }
-
- var rreq userPB.UserRegisterReq
- rreq.Users = []*sdkws.UserInfo{{UserID: uid}}
-
- for {
- err := util.ApiPost(ctx, "/auth/user_register", &rreq, nil)
- if err != nil {
- log.ZError(ctx, "post failed ,continue ", errors.New("post failed ,continue"), "register address", REGISTERADDR,
- "getAccountCheckReq", getAccountCheckReq)
- time.Sleep(100 * time.Millisecond)
- continue
- } else {
- log.ZInfo(ctx, "register ok ", "register address", REGISTERADDR, "getAccountCheckReq", getAccountCheckReq)
- userLock.Lock()
- allUserID = append(allUserID, uid)
- userLock.Unlock()
- return nil
- }
- }
-}
-
-func getToken(uid string) string {
- ctx = ccontext.WithInfo(context.Background(), &ccontext.GlobalConfig{
- UserID: uid,
- Token: "",
- IMConfig: sdk_struct.IMConfig{
- PlatformID: PlatformID,
- ApiAddr: APIADDR,
- WsAddr: WSADDR,
- LogLevel: LogLevel,
- },
- })
- ctx = ccontext.WithOperationID(ctx, utils.OperationIDGenerator())
- url := TOKENADDR
- req := authPB.UserTokenReq{
- Secret: SECRET,
- PlatformID: PlatformID,
- UserID: uid,
- }
- resp := authPB.UserTokenResp{}
- err := util.ApiPost(ctx, "/auth/user_token", &req, &resp)
- if err != nil {
- log.ZError(ctx, "Post2Api failed ", errors.New("Post2Api failed "), "userID", req.UserID, "url", url, "req", req)
- return ""
- }
-
- log.ZInfo(ctx, "get token: ", "userID", req.UserID, "token", resp.Token)
- return resp.Token
-}
-
-func RunGetToken(strMyUid string) string {
- var token string
- for true {
- token = getToken(strMyUid)
- if token == "" {
- time.Sleep(time.Duration(100) * time.Millisecond)
- continue
- } else {
- break
- }
- }
- return token
-}
-
-func getMyIP() string {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- log.ZError(ctx, "InterfaceAddrs failed ", errors.New("InterfaceAddrs failed "), "addrs", addrs)
- os.Exit(1)
- return ""
- }
- for _, address := range addrs {
- if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
- if ipnet.IP.To4() != nil {
- return ipnet.IP.String()
- }
- }
- }
- return ""
-}
-
-func RegisterReliabilityUser(id int, timeStamp string) {
- userID := GenUid(id, "reliability_"+timeStamp)
- register(userID)
- token := RunGetToken(userID)
- coreMgrLock.Lock()
- defer coreMgrLock.Unlock()
- allLoginMgr[id] = &CoreNode{token: token, userID: userID}
-}
-
-func WorkGroupRegisterReliabilityUser(id int) {
- userID := GenUid(id, "workgroup")
- // register(userID)
- token := RunGetToken(userID)
- coreMgrLock.Lock()
- defer coreMgrLock.Unlock()
- log.ZInfo(ctx, "WorkGroupRegisterReliabilityUser : ", "userID", userID, "token: ", token)
- allLoginMgr[id] = &CoreNode{token: token, userID: userID}
-}
-
-func RegisterPressUser(id int) {
- userID := GenUid(id, "press")
- register(userID)
- token := RunGetToken(userID)
- coreMgrLock.Lock()
- defer coreMgrLock.Unlock()
- allLoginMgr[id] = &CoreNode{token: token, userID: userID}
-}
diff --git a/testv2/callback.go b/test/callback.go
similarity index 98%
rename from testv2/callback.go
rename to test/callback.go
index 32051d4c1..e31b60ebe 100644
--- a/testv2/callback.go
+++ b/test/callback.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import "fmt"
diff --git a/test/config.go b/test/config.go
index eeed3e6c4..4aabad912 100644
--- a/test/config.go
+++ b/test/config.go
@@ -14,62 +14,17 @@
package test
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "sync"
-)
-
-var LogLevel uint32 = 5
-var PlatformID = int32(3)
-var LogName = ""
-var IsLogStandardOutput = true
-var LogFilePath = ""
-
-var ReliabilityUserA = 1234567
-var ReliabilityUserB = 1234567
-var (
- //TESTIP = "121.5.182.23"
- TESTIP = "59.36.173.89"
- //TESTIP = "121.37.25.71"
+import "github.com/openimsdk/protocol/constant"
- //TESTIP = "open-im-test.rentsoft.cn"
- APIADDR = "http://" + TESTIP + ":10002"
+const (
+ APIADDR = "http://127.0.0.1:10002"
+ WSADDR = "ws://127.0.0.1:10001"
- WSADDR = "ws://" + TESTIP + ":10001"
- REGISTERADDR = APIADDR + "/auth/user_register"
- TOKENADDR = APIADDR + "/auth/user_token"
- SECRET = "openIM123"
- SENDINTERVAL = 20
- GETSELFUSERINFO = APIADDR + "/user/get_self_user_info"
- CREATEGROUP = APIADDR + constant.CreateGroupRouter
- ACCOUNTCHECK = APIADDR + "/user/account_check"
- GETGROUPSINFOROUTER = APIADDR + constant.GetGroupsInfoRouter
+ UserID = "2237746339"
)
-var coreMgrLock sync.RWMutex
-
-var allLoginMgr map[int]*CoreNode
-
-var allLoginMgrtmp []*CoreNode
-
-var userLock sync.RWMutex
-
-var allUserID []string
-var allToken []string
-
-// var allWs []*interaction.Ws
-var sendSuccessCount, sendFailedCount int
-var sendSuccessLock sync.RWMutex
-var sendFailedLock sync.RWMutex
-
-var msgNumInOneClient = 0
-
-// var Msgwg sync.WaitGroup
-var sendMsgClient = 0
-
-var MaxNumGoroutine = 100000
-
-// 常量
-var (
- RELIABILITY = "reliability_"
+const (
+ PlatformID = constant.LinuxPlatformID
+ Secret = "openIM123"
+ AdminUserID = "imAdmin"
)
diff --git a/testv2/conversation_test.go b/test/conversation_test.go
similarity index 74%
rename from testv2/conversation_test.go
rename to test/conversation_test.go
index 27c85fe57..73eaa78a0 100644
--- a/testv2/conversation_test.go
+++ b/test/conversation_test.go
@@ -12,14 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
"context"
+ "testing"
+ "time"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/cache"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "testing"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/tools/log"
)
func Test_GetAllConversationList(t *testing.T) {
@@ -30,6 +35,8 @@ func Test_GetAllConversationList(t *testing.T) {
for _, conversation := range conversations {
t.Log(conversation)
}
+ t.Log(len(conversations))
+ time.Sleep(time.Second * 100)
}
func Test_GetConversationListSplit(t *testing.T) {
@@ -42,13 +49,6 @@ func Test_GetConversationListSplit(t *testing.T) {
}
}
-//func Test_SetConversationRecvMessageOpt(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Conversation().SetConversationRecvMessageOpt(ctx, []string{"asdasd"}, 1)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
func Test_HideConversation(t *testing.T) {
err := open_im_sdk.UserForSDK.Conversation().HideConversation(ctx, "asdasd")
if err != nil {
@@ -56,15 +56,15 @@ func Test_HideConversation(t *testing.T) {
}
}
-func Test_GetConversationRecvMessageOpt(t *testing.T) {
- opts, err := open_im_sdk.UserForSDK.Conversation().GetConversationRecvMessageOpt(ctx, []string{"asdasd"})
- if err != nil {
- t.Fatal(err)
- }
- for _, v := range opts {
- t.Log(v.ConversationID, *v.Result)
- }
-}
+//func Test_GetConversationRecvMessageOpt(t *testing.T) {
+// opts, err := open_im_sdk.UserForSDK.Conversation().GetConversationRecvMessageOpt(ctx, []string{"asdasd"})
+// if err != nil {
+// t.Fatal(err)
+// }
+// for _, v := range opts {
+// t.Log(v.ConversationID, *v.Execute)
+// }
+//}
func Test_GetGlobalRecvMessageOpt(t *testing.T) {
opt, err := open_im_sdk.UserForSDK.Conversation().GetOneConversation(ctx, 2, "1772958501")
@@ -84,15 +84,6 @@ func Test_GetGetMultipleConversation(t *testing.T) {
}
}
-// funcation Test_DeleteConversation(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Conversation().DeleteConversation(ctx, "group_17729585012")
-// if err != nil {
-// if !strings.Contains(err.Error(), "no update") {
-// t.Fatal(err)
-// }
-// }
-// }
-
func Test_SetConversationDraft(t *testing.T) {
err := open_im_sdk.UserForSDK.Conversation().SetConversationDraft(ctx, "group_17729585012", "draft")
if err != nil {
@@ -100,36 +91,8 @@ func Test_SetConversationDraft(t *testing.T) {
}
}
-func Test_ResetConversationGroupAtType(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().ResetConversationGroupAtType(ctx, "group_17729585012")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_PinConversation(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().PinConversation(ctx, "group_17729585012", true)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SetOneConversationPrivateChat(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().SetOneConversationPrivateChat(ctx, "single_3411008330", true)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SetOneConversationBurnDuration(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().SetOneConversationBurnDuration(ctx, "single_3411008330", 10)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SetOneConversationRecvMessageOpt(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().SetOneConversationRecvMessageOpt(ctx, "single_3411008330", 1)
+func Test_SetConversation(t *testing.T) {
+ err := open_im_sdk.UserForSDK.Conversation().SetConversation(ctx, "group_17729585012", &conversation.ConversationReq{})
if err != nil {
t.Fatal(err)
}
@@ -152,15 +115,6 @@ func Test_SendMessage(t *testing.T) {
}
}
-func Test_SendMessageNotOss(t *testing.T) {
- ctx = context.WithValue(ctx, "callback", TestSendMsg{})
- msg, _ := open_im_sdk.UserForSDK.Conversation().CreateTextMessage(ctx, "textMsg")
- _, err := open_im_sdk.UserForSDK.Conversation().SendMessageNotOss(ctx, msg, "3411008330", "", nil, false)
- if err != nil {
- t.Fatal(err)
- }
-}
-
func Test_FindMessageList(t *testing.T) {
msgs, err := open_im_sdk.UserForSDK.Conversation().FindMessageList(ctx, []*sdk_params_callback.ConversationArgs{})
if err != nil {
@@ -173,7 +127,12 @@ func Test_FindMessageList(t *testing.T) {
}
func Test_GetAdvancedHistoryMessageList(t *testing.T) {
- msgs, err := open_im_sdk.UserForSDK.Conversation().GetAdvancedHistoryMessageList(ctx, sdk_params_callback.GetAdvancedHistoryMessageListParams{})
+ msgs, err := open_im_sdk.UserForSDK.Conversation().GetAdvancedHistoryMessageList(ctx, sdk_params_callback.GetAdvancedHistoryMessageListParams{
+ ConversationID: "si_5318543822_9511766539",
+ StartClientMsgID: "",
+ Count: 40,
+ ViewType: 0,
+ })
if err != nil {
t.Fatal(err)
}
@@ -183,10 +142,16 @@ func Test_GetAdvancedHistoryMessageList(t *testing.T) {
}
func Test_GetAdvancedHistoryMessageListReverse(t *testing.T) {
- msgs, err := open_im_sdk.UserForSDK.Conversation().GetAdvancedHistoryMessageListReverse(ctx, sdk_params_callback.GetAdvancedHistoryMessageListParams{})
+ msgs, err := open_im_sdk.UserForSDK.Conversation().GetAdvancedHistoryMessageListReverse(ctx, sdk_params_callback.GetAdvancedHistoryMessageListParams{
+ ConversationID: "si_3325086438_5054969402",
+ StartClientMsgID: "91e40552a05a60494a56e86d36c497ce",
+ Count: 20,
+ ViewType: cache.ViewHistory,
+ })
if err != nil {
t.Fatal(err)
}
+ log.ZDebug(context.Background(), "GetAdvancedHistoryMessageListReverse Resp", "resp", msgs)
for _, v := range msgs.MessageList {
t.Log(v)
}
@@ -207,7 +172,24 @@ func Test_InsertGroupMessageToLocalStorage(t *testing.T) {
}
func Test_SearchLocalMessages(t *testing.T) {
- msgs, err := open_im_sdk.UserForSDK.Conversation().SearchLocalMessages(ctx, &sdk_params_callback.SearchLocalMessagesParams{})
+ // req := &sdk_params_callback.SearchLocalMessagesParams{
+ // Count: 20,
+ // KeywordList: []string{"1"},
+ // MessageTypeList: []int{105},
+ // PageIndex: 1,
+ // SenderUserIDList: []string{},
+ // }
+
+ req := &sdk_params_callback.SearchLocalMessagesParams{
+ KeywordList: []string{"1"},
+ ConversationID: "sg_3161900504",
+ MessageTypeList: []int{105},
+ PageIndex: 1,
+ Count: 20,
+ SenderUserIDList: []string{"1695766238"},
+ }
+
+ msgs, err := open_im_sdk.UserForSDK.Conversation().SearchLocalMessages(ctx, req)
if err != nil {
t.Fatal(err)
}
@@ -224,7 +206,7 @@ func Test_SetMessageLocalEx(t *testing.T) {
}
func Test_DeleteAllMsgFromLocalAndSvr(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().DeleteAllMsgFromLocalAndSvr(ctx)
+ err := open_im_sdk.UserForSDK.Conversation().DeleteAllMsgFromLocalAndServer(ctx)
if err != nil {
t.Fatal(err)
}
@@ -237,6 +219,13 @@ func Test_DeleteAllMessageFromLocalStorage(t *testing.T) {
}
}
+func Test_DeleteMessage(t *testing.T) {
+ err := open_im_sdk.UserForSDK.Conversation().DeleteMessage(ctx, "si_1695766238_5099153716", "8b67803979bce9c6daf82fb64dbffc5f")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
func Test_ClearConversationAndDeleteAllMsg(t *testing.T) {
err := open_im_sdk.UserForSDK.Conversation().ClearConversationAndDeleteAllMsg(ctx, "si_3271407977_7152307910")
if err != nil {
@@ -244,6 +233,13 @@ func Test_ClearConversationAndDeleteAllMsg(t *testing.T) {
}
}
+func Test_DeleteConversationAndDeleteAllMsg(t *testing.T) {
+ err := open_im_sdk.UserForSDK.Conversation().DeleteConversationAndDeleteAllMsg(ctx, "si_3271407977_7152307910")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
// func Test_RevokeMessage(t *testing.T) {
// err := open_im_sdk.UserForSDK.Conversation().RevokeMessage(ctx, &sdk_struct.MsgStruct{SessionType: 1, ContentType: 101,
// ClientMsgID: "380e2eb1709875340d769880982ebb21", Seq: 57, SendID: "9169012630", RecvID: "2456093263"})
@@ -260,6 +256,13 @@ func Test_MarkConversationMessageAsRead(t *testing.T) {
}
}
+func Test_MarkAllConversationMessageAsRead(t *testing.T) {
+ err := open_im_sdk.UserForSDK.Conversation().MarkAllConversationMessageAsRead(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
func Test_MarkMsgsAsRead(t *testing.T) {
err := open_im_sdk.UserForSDK.Conversation().MarkMessagesAsReadByMsgID(ctx, "si_2688118337_7249315132",
[]string{"fb56ed151b675e0837ed3af79dbf66b1",
@@ -271,7 +274,7 @@ func Test_MarkMsgsAsRead(t *testing.T) {
func Test_SendImgMsg(t *testing.T) {
ctx = context.WithValue(ctx, "callback", TestSendMsg{})
- msg, err := open_im_sdk.UserForSDK.Conversation().CreateImageMessage(ctx, "C:\\Users\\Admin\\Desktop\\test.png")
+ msg, err := open_im_sdk.UserForSDK.Conversation().CreateImageMessage(ctx, "C:\\Users\\Admin\\Desktop\\test.png", &sdk_struct.PictureBaseInfo{}, &sdk_struct.PictureBaseInfo{}, &sdk_struct.PictureBaseInfo{})
if err != nil {
t.Fatal(err)
}
@@ -281,12 +284,7 @@ func Test_SendImgMsg(t *testing.T) {
}
t.Logf("send smg => %+v\n", res)
}
-func Test_SetConversationEx(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().SetOneConversationEx(ctx, "si_1_2", "abc")
- if err != nil {
- t.Fatal(err)
- }
-}
+
func Test_SearchConversation(t *testing.T) {
result, err := open_im_sdk.UserForSDK.Conversation().SearchConversation(ctx, "a")
if err != nil {
diff --git a/testv2/create_msg_test.go b/test/create_msg_test.go
similarity index 74%
rename from testv2/create_msg_test.go
rename to test/create_msg_test.go
index d91971c5f..633d5ec5d 100644
--- a/testv2/create_msg_test.go
+++ b/test/create_msg_test.go
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
+ "testing"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "testing"
)
func Test_CreateTextMessage(t *testing.T) {
@@ -60,14 +61,6 @@ func Test_CreateAdvancedQuoteMessage(t *testing.T) {
t.Log(message)
}
-func Test_CreateVideoMessageFromFullPath(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateVideoMessageFromFullPath(ctx, ".\\test.png", "mp4", 10, ".\\test.png")
- if err != nil {
- t.Error(err)
- }
- t.Log(message)
-}
-
func Test_CreateCardMessage(t *testing.T) {
message, err := open_im_sdk.UserForSDK.Conversation().CreateCardMessage(ctx, &sdk_struct.CardElem{
UserID: "123456",
@@ -81,24 +74,8 @@ func Test_CreateCardMessage(t *testing.T) {
}
func Test_CreateImageMessage(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateImageMessage(ctx, ".\\test.png")
- if err != nil {
- t.Error(err)
- }
- t.Log(message)
-}
-
-func Test_CreateImageMessageByURL(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateImageMessageByURL(ctx, "",
- sdk_struct.PictureBaseInfo{}, sdk_struct.PictureBaseInfo{}, sdk_struct.PictureBaseInfo{})
- if err != nil {
- t.Error(err)
- }
- t.Log(message)
-}
-
-func Test_CreateSoundMessageByURL(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateSoundMessageByURL(ctx, &sdk_struct.SoundBaseInfo{})
+ message, err := open_im_sdk.UserForSDK.Conversation().CreateImageMessage(ctx, ".\\test.png",
+ &sdk_struct.PictureBaseInfo{}, &sdk_struct.PictureBaseInfo{}, &sdk_struct.PictureBaseInfo{})
if err != nil {
t.Error(err)
}
@@ -106,7 +83,7 @@ func Test_CreateSoundMessageByURL(t *testing.T) {
}
func Test_CreateSoundMessage(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateSoundMessage(ctx, ".\\test.png", 20)
+ message, err := open_im_sdk.UserForSDK.Conversation().CreateSoundMessage(ctx, ".\\test.png", 20, &sdk_struct.SoundBaseInfo{})
if err != nil {
t.Error(err)
}
@@ -114,15 +91,7 @@ func Test_CreateSoundMessage(t *testing.T) {
}
func Test_CreateVideoMessage(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateVideoMessage(ctx, ".\\test.png", "mp4", 10, "")
- if err != nil {
- t.Error(err)
- }
- t.Log(message)
-}
-
-func Test_CreateVideoMessageByURL(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateVideoMessageByURL(ctx, sdk_struct.VideoBaseInfo{})
+ message, err := open_im_sdk.UserForSDK.Conversation().CreateVideoMessage(ctx, ".\\test.png", "mp4", 10, ".\\test.png", &sdk_struct.VideoBaseInfo{})
if err != nil {
t.Error(err)
}
@@ -130,15 +99,7 @@ func Test_CreateVideoMessageByURL(t *testing.T) {
}
func Test_CreateFileMessage(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateFileMessage(ctx, ".\\test.png", "png")
- if err != nil {
- t.Error(err)
- }
- t.Log(message)
-}
-
-func Test_CreateFileMessageByURL(t *testing.T) {
- message, err := open_im_sdk.UserForSDK.Conversation().CreateFileMessageByURL(ctx, sdk_struct.FileBaseInfo{})
+ message, err := open_im_sdk.UserForSDK.Conversation().CreateFileMessage(ctx, ".\\test.png", "png", &sdk_struct.FileBaseInfo{})
if err != nil {
t.Error(err)
}
diff --git a/testv2/empty_test.go b/test/empty_test.go
similarity index 68%
rename from testv2/empty_test.go
rename to test/empty_test.go
index fa94f1ae9..810b6595a 100644
--- a/testv2/empty_test.go
+++ b/test/empty_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
@@ -21,18 +21,18 @@ import (
"time"
)
-func Test_ChangeInputState(t *testing.T) {
+func Test_Empty(t *testing.T) {
for {
- err := open_im_sdk.UserForSDK.Conversation().ChangeInputStates(ctx, "sg_2309860938", true)
- if err != nil {
- log.ZError(ctx, "ChangeInputStates", err)
- }
time.Sleep(time.Second * 1)
}
}
-func Test_Empty(t *testing.T) {
+func Test_ChangeInputState(t *testing.T) {
for {
+ err := open_im_sdk.UserForSDK.Conversation().ChangeInputStates(ctx, "sg_2309860938", true)
+ if err != nil {
+ log.ZError(ctx, "ChangeInputStates", err)
+ }
time.Sleep(time.Second * 1)
}
}
@@ -40,3 +40,21 @@ func Test_Empty(t *testing.T) {
func Test_RunWait(t *testing.T) {
time.Sleep(time.Second * 10)
}
+
+func Test_OnlineState(t *testing.T) {
+ defer func() { select {} }()
+ userIDs := []string{
+ //"3611802798",
+ "2110910952",
+ }
+ for i := 1; ; i++ {
+ time.Sleep(time.Second)
+ //open_im_sdk.UserForSDK.LongConnMgr().UnsubscribeUserOnlinePlatformIDs(ctx, userIDs)
+ res, err := open_im_sdk.UserForSDK.LongConnMgr().GetUserOnlinePlatformIDs(ctx, userIDs)
+ if err != nil {
+ t.Logf("@@@@@@@@@@@@=====> <%d> error %s", i, err)
+ continue
+ }
+ t.Logf("@@@@@@@@@@@@=====> <%d> success %+v", i, res)
+ }
+}
diff --git a/testv2/file_test.go b/test/file_test.go
similarity index 92%
rename from testv2/file_test.go
rename to test/file_test.go
index c180d7375..cdfbba349 100644
--- a/testv2/file_test.go
+++ b/test/file_test.go
@@ -12,13 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"path/filepath"
"testing"
+
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
)
func TestUploadFile(t *testing.T) {
diff --git a/test/friend_test.go b/test/friend_test.go
new file mode 100644
index 000000000..3f1461f27
--- /dev/null
+++ b/test/friend_test.go
@@ -0,0 +1,164 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package test
+
+//
+//import (
+// "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+// "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
+// "github.com/openimsdk/protocol/wrapperspb"
+// "testing"
+// "time"
+//
+// friend2 "github.com/openimsdk/protocol/relation"
+//)
+//
+//func Test_GetSpecifiedFriendsInfo(t *testing.T) {
+// info, err := open_im_sdk.UserForSDK.Friend().GetSpecifiedFriendsInfo(ctx, []string{"45644221123"})
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("GetDesignatedFriendsInfo success", ctx.Value("operationID"))
+// for _, userInfo := range info {
+// t.Log(userInfo)
+// }
+//}
+//
+//func Test_AddFriend(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().AddFriend(ctx, &friend2.ApplyToAddFriendReq{
+// ToUserID: "2",
+// ReqMsg: "test add",
+// Ex: "add",
+// })
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("AddFriend success", ctx.Value("operationID"))
+//}
+//
+//
+//func Test_AcceptFriendApplication(t *testing.T) {
+// req := &sdk_params_callback.ProcessFriendApplicationParams{ToUserID: "1", HandleMsg: "test accept"}
+// err := open_im_sdk.UserForSDK.Friend().AcceptFriendApplication(ctx, req)
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("AcceptFriendApplication success", ctx.Value("operationID"))
+// time.Sleep(time.Second * 30)
+//}
+//
+//func Test_RefuseFriendApplication(t *testing.T) {
+// req := &sdk_params_callback.ProcessFriendApplicationParams{ToUserID: "6754269405", HandleMsg: "test refuse"}
+// err := open_im_sdk.UserForSDK.Friend().RefuseFriendApplication(ctx, req)
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("RefuseFriendApplication success", ctx.Value("operationID"))
+// time.Sleep(time.Second * 30)
+//}
+//
+//func Test_CheckFriend(t *testing.T) {
+// res, err := open_im_sdk.UserForSDK.Friend().CheckFriend(ctx, []string{"863454357", "45644221123"})
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("CheckFriend success", ctx.Value("operationID"))
+// for _, re := range res {
+// t.Log(re)
+// }
+//}
+//func Test_PinFriend(t *testing.T) {
+// pinParams := &sdk_params_callback.SetFriendPinParams{
+// ToUserIDs: []string{"2", "3"},
+// IsPinned: &wrapperspb.BoolValue{Value: false},
+// }
+//
+// err := open_im_sdk.UserForSDK.Friend().PinFriends(ctx, pinParams)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("CheckFriend success", ctx.Value("operationID"))
+//}
+//func Test_DeleteFriend(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().DeleteFriend(ctx, "863454357")
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("DeleteFriend success", ctx.Value("operationID"))
+//}
+//
+//func Test_GetFriendList(t *testing.T) {
+// infos, err := open_im_sdk.UserForSDK.Friend().GetFriendList(ctx)
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("GetFriendList success", ctx.Value("operationID"))
+// for _, info := range infos {
+// t.Logf("PublicInfo: %#v, FriendInfo: %#v, BlackInfo: %#v", info.PublicInfo, info.FriendInfo, info.BlackInfo)
+// }
+//}
+//
+//func Test_SearchFriends(t *testing.T) {
+// info, err := open_im_sdk.UserForSDK.Friend().SearchFriends(ctx, &sdk_params_callback.SearchFriendsParam{KeywordList: []string{"863454357"}, IsSearchUserID: true})
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("SearchFriends success", ctx.Value("operationID"))
+// for _, item := range info {
+// t.Log(*item)
+// }
+//}
+//
+//func Test_SetFriendRemark(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().SetFriendRemark(ctx, &sdk_params_callback.SetFriendRemarkParams{ToUserID: "863454357", Remark: "testRemark"})
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("SetFriendRemark success", ctx.Value("operationID"))
+//}
+//
+//func Test_AddBlack(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().AddBlack(ctx, "863454357", "ex")
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("AddBlack success", ctx.Value("operationID"))
+//}
+//
+//func Test_RemoveBlack(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().RemoveBlack(ctx, "863454357")
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("RemoveBlack success", ctx.Value("operationID"))
+//}
+//
+//func Test_GetBlackList(t *testing.T) {
+// info, err := open_im_sdk.UserForSDK.Friend().GetBlackList(ctx)
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log("GetBlackList success", ctx.Value("operationID"))
+// for _, item := range info {
+// t.Log(*item)
+// }
+//}
+//func Test_SetFriendsEx(t *testing.T) {
+// err := open_im_sdk.UserForSDK.Friend().SetFriendsEx(ctx, []string{"2"}, "exx")
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
diff --git a/testv2/group_test.go b/test/group_test.go
similarity index 87%
rename from testv2/group_test.go
rename to test/group_test.go
index 07db02d5d..7899eebf3 100644
--- a/testv2/group_test.go
+++ b/test/group_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
"testing"
@@ -98,25 +98,7 @@ func Test_CancelChangeGroupMemberMute(t *testing.T) {
t.Log("CancelChangeGroupMemberMute success", ctx.Value("operationID"))
}
-func Test_SetGroupMemberRoleLevel(t *testing.T) {
- // 1:普通成员 2:群主 3:管理员
- err := open_im_sdk.UserForSDK.Group().SetGroupMemberRoleLevel(ctx, "3459296007", "45644221123", 1)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("SetGroupMemberRoleLevel success", ctx.Value("operationID"))
-}
-
-func Test_SetGroupMemberNickname(t *testing.T) {
- err := open_im_sdk.UserForSDK.Group().SetGroupMemberNickname(ctx, "3459296007", "45644221123", "test1234")
- if err != nil {
- t.Fatal(err)
- }
- t.Log("SetGroupMemberNickname success", ctx.Value("operationID"))
-}
-
func Test_SetGroupMemberInfo(t *testing.T) {
- // 1:普通成员 2:群主 3:管理员
err := open_im_sdk.UserForSDK.Group().SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{
GroupID: "3889561099",
UserID: UserID,
@@ -241,14 +223,6 @@ func Test_InviteUserToGroup(t *testing.T) {
t.Log("InviteUserToGroup success", ctx.Value("operationID"))
}
-//func Test_SyncGroup(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Group().SyncGroupMember(ctx, "3179997540")
-// if err != nil {
-// t.Fatal(err)
-// }
-// time.Sleep(time.Second * 100000)
-//}
-
func Test_GetGroup(t *testing.T) {
t.Log("--------------------------")
infos, err := open_im_sdk.UserForSDK.Group().GetSpecifiedGroupsInfo(ctx, []string{"3179997540"})
@@ -283,9 +257,9 @@ func Test_IsJoinGroup(t *testing.T) {
func Test_GetGroupMemberList(t *testing.T) {
t.Log("--------------------------")
m := map[int32]string{
- constant.GroupOwner: "群主",
- constant.GroupAdmin: "管理员",
- constant.GroupOrdinaryUsers: "成员",
+ constant.GroupOwner: "Group Owner",
+ constant.GroupAdmin: "Administrator",
+ constant.GroupOrdinaryUsers: "Members",
}
members, err := open_im_sdk.UserForSDK.Group().GetGroupMemberList(ctx, "3889561099", 0, 0, 9999999)
@@ -300,14 +274,8 @@ func Test_GetGroupMemberList(t *testing.T) {
t.Log("--------------------------")
}
-func Test_SyncAllGroupMember(t *testing.T) {
- err := open_im_sdk.UserForSDK.Group().SyncAllGroupMember(ctx, "3889561099")
- if err != nil {
- t.Fatal(err)
- }
-}
func Test_SetGroupInfo(t *testing.T) {
- err := open_im_sdk.UserForSDK.Group().SetGroupInfo(ctx, &sdkws.GroupInfoForSet{
+ err := open_im_sdk.UserForSDK.Group().SetGroupInfo(ctx, &group.SetGroupInfoExReq{
GroupID: "3889561099",
Ex: &wrapperspb.StringValue{Value: "groupex"},
})
diff --git a/test/init.go b/test/init.go
new file mode 100644
index 000000000..9f97aa850
--- /dev/null
+++ b/test/init.go
@@ -0,0 +1,101 @@
+// Copyright © 2023 OpenIM SDK. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package test
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "math/rand"
+ "strconv"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/api"
+ "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
+ "github.com/openimsdk/protocol/auth"
+
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
+)
+
+var ctx context.Context
+
+func init() {
+ fmt.Println("------------------------>>>>>>>>>>>>>>>>>>> test init func <<<<<<<<<<<<<<<<<<<------------------------")
+ rand.Seed(time.Now().UnixNano())
+ listner := &OnConnListener{}
+ config := getConf(APIADDR, WSADDR)
+ config.DataDir = "./"
+ configData, err := json.Marshal(config)
+ if err != nil {
+ panic(err)
+ }
+ isInit := open_im_sdk.InitSDK(listner, "test", string(configData))
+ if !isInit {
+ panic("init sdk failed")
+ }
+ ctx = open_im_sdk.UserForSDK.Context()
+ ctx = ccontext.WithOperationID(ctx, "initOperationID_"+strconv.Itoa(int(time.Now().UnixMilli())))
+ token, err := GetUserToken(ctx, UserID, PlatformID, Secret, config)
+ if err != nil {
+ panic(err)
+ }
+ if err := open_im_sdk.UserForSDK.Login(ctx, UserID, token); err != nil {
+ panic(err)
+ }
+ ch := make(chan error)
+ open_im_sdk.UserForSDK.SetConversationListener(&onConversationListener{ctx: ctx, ch: ch})
+ open_im_sdk.UserForSDK.SetGroupListener(&onGroupListener{ctx: ctx})
+ open_im_sdk.UserForSDK.SetAdvancedMsgListener(&onAdvancedMsgListener{ctx: ctx})
+ open_im_sdk.UserForSDK.SetFriendshipListener(&onFriendshipListener{ctx: ctx})
+ open_im_sdk.UserForSDK.SetUserListener(&onUserListener{ctx: ctx})
+ if err := <-ch; err != nil {
+ panic(err)
+ }
+}
+
+func getConf(APIADDR, WSADDR string) sdk_struct.IMConfig {
+ var cf sdk_struct.IMConfig
+ cf.ApiAddr = APIADDR
+ cf.WsAddr = WSADDR
+ cf.DataDir = "./"
+ cf.LogLevel = 6
+ cf.IsExternalExtensions = true
+ cf.PlatformID = PlatformID
+ cf.LogFilePath = "./"
+ cf.IsLogStandardOutput = true
+ return cf
+}
+
+func GetUserToken(ctx context.Context, userID string, platformID int32, secret string, imConf sdk_struct.IMConfig) (string, error) {
+ adminReq := &auth.GetAdminTokenReq{
+ UserID: AdminUserID,
+ Secret: secret,
+ }
+ adminToken, err := api.ExtractField(ctx, api.GetAdminToken.Invoke, adminReq, (*auth.GetAdminTokenResp).GetToken)
+ if err != nil {
+ return "", err
+ }
+ userReq := &auth.GetUserTokenReq{
+ UserID: userID,
+ PlatformID: platformID,
+ }
+ ctx = ccontext.WithInfo(ctx, &ccontext.GlobalConfig{
+ UserID: userID,
+ Token: adminToken,
+ IMConfig: imConf,
+ })
+ return api.ExtractField(ctx, api.GetUsersToken.Invoke, userReq, (*auth.GetUserTokenResp).GetToken)
+}
\ No newline at end of file
diff --git a/testv2/init.go b/test/listener.go
similarity index 61%
rename from testv2/init.go
rename to test/listener.go
index 3b81e62ff..e29939a5c 100644
--- a/testv2/init.go
+++ b/test/listener.go
@@ -12,131 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
- "bytes"
"context"
- "encoding/json"
"fmt"
- "io"
- "math/rand"
- "net/http"
- "strconv"
- "time"
-
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/protocol/constant"
-
"github.com/openimsdk/tools/log"
)
-var (
- ctx context.Context
-)
+type OnConnListener struct{}
-func init() {
- fmt.Println("------------------------>>>>>>>>>>>>>>>>>>> test v2 init funcation <<<<<<<<<<<<<<<<<<<------------------------")
- rand.Seed(time.Now().UnixNano())
- listner := &OnConnListener{}
- config := getConf(APIADDR, WSADDR)
- config.DataDir = "./"
- configData, err := json.Marshal(config)
- if err != nil {
- panic(err)
- }
- isInit := open_im_sdk.InitSDK(listner, "test", string(configData))
- if !isInit {
- panic("init sdk failed")
- }
- ctx = open_im_sdk.UserForSDK.Context()
- ctx = ccontext.WithOperationID(ctx, "initOperationID_"+strconv.Itoa(int(time.Now().UnixMilli())))
- token, err := GetUserToken(ctx, UserID)
- if err != nil {
- panic(err)
- }
- if err := open_im_sdk.UserForSDK.Login(ctx, UserID, token); err != nil {
- panic(err)
- }
- open_im_sdk.UserForSDK.SetConversationListener(&onConversationListener{ctx: ctx})
- open_im_sdk.UserForSDK.SetGroupListener(&onGroupListener{ctx: ctx})
- open_im_sdk.UserForSDK.SetAdvancedMsgListener(&onAdvancedMsgListener{ctx: ctx})
- open_im_sdk.UserForSDK.SetFriendListener(&onFriendListener{ctx: ctx})
- open_im_sdk.UserForSDK.SetUserListener(&onUserListener{ctx: ctx})
- time.Sleep(time.Second * 2)
-}
-
-func GetUserToken(ctx context.Context, userID string) (string, error) {
- jsonReqData, err := json.Marshal(map[string]any{
- "userID": userID,
- "platformID": constant.LinuxPlatformID,
- "secret": "openIM123",
- })
- if err != nil {
- return "", err
- }
- path := APIADDR + "/auth/user_token"
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, path, bytes.NewReader(jsonReqData))
- if err != nil {
- return "", err
- }
- req.Header.Set("operationID", ctx.Value("operationID").(string))
- client := http.Client{Timeout: time.Second * 3}
- resp, err := client.Do(req)
- if err != nil {
- return "", err
- }
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return "", err
- }
- type Result struct {
- ErrCode int `json:"errCode"`
- ErrMsg string `json:"errMsg"`
- ErrDlt string `json:"errDlt"`
- Data struct {
- Token string `json:"token"`
- ExpireTimeSeconds int `json:"expireTimeSeconds"`
- } `json:"data"`
- }
- var result Result
- if err := json.Unmarshal(body, &result); err != nil {
- return "", err
- }
- if result.ErrCode != 0 {
- return "", fmt.Errorf("errCode:%d, errMsg:%s, errDlt:%s", result.ErrCode, result.ErrMsg, result.ErrDlt)
- }
- return result.Data.Token, nil
-}
-
-type onListenerForService struct {
- ctx context.Context
-}
+func (c *OnConnListener) OnUserTokenInvalid(errMsg string) {}
-func (o *onListenerForService) OnGroupApplicationAdded(groupApplication string) {
- log.ZInfo(o.ctx, "OnGroupApplicationAdded", "groupApplication", groupApplication)
+func (c *OnConnListener) OnConnecting() {
+ // fmt.Println("OnConnecting")
}
-func (o *onListenerForService) OnGroupApplicationAccepted(groupApplication string) {
- log.ZInfo(o.ctx, "OnGroupApplicationAccepted", "groupApplication", groupApplication)
+func (c *OnConnListener) OnConnectSuccess() {
+ // fmt.Println("OnConnectSuccess")
}
-func (o *onListenerForService) OnFriendApplicationAdded(friendApplication string) {
- log.ZInfo(o.ctx, "OnFriendApplicationAdded", "friendApplication", friendApplication)
+func (c *OnConnListener) OnConnectFailed(errCode int32, errMsg string) {
+ // fmt.Println("OnConnectFailed")
}
-func (o *onListenerForService) OnFriendApplicationAccepted(groupApplication string) {
- log.ZInfo(o.ctx, "OnFriendApplicationAccepted", "groupApplication", groupApplication)
+func (c *OnConnListener) OnKickedOffline() {
+ // fmt.Println("OnKickedOffline")
}
-func (o *onListenerForService) OnRecvNewMessage(message string) {
- log.ZInfo(o.ctx, "OnRecvNewMessage", "message", message)
+func (c *OnConnListener) OnUserTokenExpired() {
+ // fmt.Println("OnUserTokenExpired")
}
type onConversationListener struct {
ctx context.Context
+ ch chan error
}
func (o *onConversationListener) OnSyncServerStart(reinstalled bool) {
@@ -145,10 +55,12 @@ func (o *onConversationListener) OnSyncServerStart(reinstalled bool) {
func (o *onConversationListener) OnSyncServerFinish(reinstalled bool) {
log.ZInfo(o.ctx, "OnSyncServerFinish")
+ o.ch <- nil
}
func (o *onConversationListener) OnSyncServerFailed(reinstalled bool) {
log.ZInfo(o.ctx, "OnSyncServerFailed")
+ o.ch <- fmt.Errorf("OnSyncServerFailed")
}
func (o *onConversationListener) OnSyncServerProgress(progress int) {
@@ -160,7 +72,7 @@ func (o *onConversationListener) OnNewConversation(conversationList string) {
}
func (o *onConversationListener) OnConversationChanged(conversationList string) {
- log.ZInfo(o.ctx, "OnConversationChanged", "conversationList", conversationList)
+ log.ZInfo(o.ctx, "OnConversationChanged", "####### conversationList", conversationList)
}
func (o *onConversationListener) OnTotalUnreadMessageCountChanged(totalUnreadCount int32) {
@@ -236,13 +148,9 @@ func (o *onAdvancedMsgListener) OnMsgDeleted(message string) {
log.ZInfo(o.ctx, "OnMsgDeleted", "message", message)
}
-//funcation (o *onAdvancedMsgListener) OnMsgDeleted(messageList string) {
-// log.ZInfo(o.ctx, "OnRecvOfflineNewMessages", "messageList", messageList)
-//}
-//
-//funcation (o *onAdvancedMsgListener) OnMsgDeleted(message string) {
-// log.ZInfo(o.ctx, "OnMsgDeleted", "message", message)
-//}
+func (o *onAdvancedMsgListener) OnMsgEdited(message string) {
+ log.ZInfo(o.ctx, "OnMsgEdited", "######## message", message)
+}
func (o *onAdvancedMsgListener) OnRecvOfflineNewMessages(messageList string) {
log.ZInfo(o.ctx, "OnRecvOfflineNewMessages", "messageList", messageList)
@@ -280,43 +188,43 @@ func (o *onAdvancedMsgListener) OnRecvMessageExtensionsAdded(msgID string, react
log.ZInfo(o.ctx, "OnRecvMessageExtensionsAdded", "msgID", msgID, "reactionExtensionList", reactionExtensionList)
}
-type onFriendListener struct {
+type onFriendshipListener struct {
ctx context.Context
}
-func (o *onFriendListener) OnFriendApplicationAdded(friendApplication string) {
+func (o *onFriendshipListener) OnFriendApplicationAdded(friendApplication string) {
log.ZDebug(context.Background(), "OnFriendApplicationAdded", "friendApplication", friendApplication)
}
-func (o *onFriendListener) OnFriendApplicationDeleted(friendApplication string) {
+func (o *onFriendshipListener) OnFriendApplicationDeleted(friendApplication string) {
log.ZDebug(context.Background(), "OnFriendApplicationDeleted", "friendApplication", friendApplication)
}
-func (o *onFriendListener) OnFriendApplicationAccepted(friendApplication string) {
+func (o *onFriendshipListener) OnFriendApplicationAccepted(friendApplication string) {
log.ZDebug(context.Background(), "OnFriendApplicationAccepted", "friendApplication", friendApplication)
}
-func (o *onFriendListener) OnFriendApplicationRejected(friendApplication string) {
+func (o *onFriendshipListener) OnFriendApplicationRejected(friendApplication string) {
log.ZDebug(context.Background(), "OnFriendApplicationRejected", "friendApplication", friendApplication)
}
-func (o *onFriendListener) OnFriendAdded(friendInfo string) {
+func (o *onFriendshipListener) OnFriendAdded(friendInfo string) {
log.ZDebug(context.Background(), "OnFriendAdded", "friendInfo", friendInfo)
}
-func (o *onFriendListener) OnFriendDeleted(friendInfo string) {
+func (o *onFriendshipListener) OnFriendDeleted(friendInfo string) {
log.ZDebug(context.Background(), "OnFriendDeleted", "friendInfo", friendInfo)
}
-func (o *onFriendListener) OnFriendInfoChanged(friendInfo string) {
+func (o *onFriendshipListener) OnFriendInfoChanged(friendInfo string) {
log.ZDebug(context.Background(), "OnFriendInfoChanged", "friendInfo", friendInfo)
}
-func (o *onFriendListener) OnBlackAdded(blackInfo string) {
+func (o *onFriendshipListener) OnBlackAdded(blackInfo string) {
log.ZDebug(context.Background(), "OnBlackAdded", "blackInfo", blackInfo)
}
-func (o *onFriendListener) OnBlackDeleted(blackInfo string) {
+func (o *onFriendshipListener) OnBlackDeleted(blackInfo string) {
log.ZDebug(context.Background(), "OnBlackDeleted", "blackInfo", blackInfo)
}
diff --git a/test/login.go b/test/login.go
deleted file mode 100644
index e457db619..000000000
--- a/test/login.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/log"
- "time"
-)
-
-type BaseSuccessFailed struct {
- successData string
- errCode int
- errMsg string
- funcName string
- time time.Time
-}
-
-func (b *BaseSuccessFailed) OnError(errCode int32, errMsg string) {
- b.errCode = -1
- b.errMsg = errMsg
- log.ZError(ctx, "login failed", errors.New("login failed"), "errCode", errCode, "errMsg", errMsg)
-
-}
-
-func (b *BaseSuccessFailed) OnSuccess(data string) {
- b.errCode = 1
- b.successData = data
- log.ZInfo(ctx, "login success", "data", data, "time since", time.Since(b.time))
-}
-
-func InOutDoTest(uid, tk, ws, api string) {
- var cf sdk_struct.IMConfig
- cf.ApiAddr = api
- cf.PlatformID = constant.WindowsPlatformID
- cf.WsAddr = ws
- cf.DataDir = "./"
- cf.LogLevel = LogLevel
- cf.IsExternalExtensions = true
- cf.IsLogStandardOutput = true
- cf.LogFilePath = "./"
-
- b, _ := json.Marshal(cf)
- s := string(b)
- fmt.Println(s)
- var testinit testInitLister
-
- operationID := utils.OperationIDGenerator()
- if !open_im_sdk.InitSDK(&testinit, operationID, s) {
- fmt.Println("", "InitSDK failed")
- return
- }
-
- var testConversation conversationCallBack
- open_im_sdk.SetConversationListener(&testConversation)
-
- var testUser userCallback
- open_im_sdk.SetUserListener(testUser)
-
- var msgCallBack MsgListenerCallBak
- open_im_sdk.SetAdvancedMsgListener(&msgCallBack)
-
- var batchMsg BatchMsg
- open_im_sdk.SetBatchMsgListener(&batchMsg)
-
- var friendListener testFriendListener
- open_im_sdk.SetFriendListener(friendListener)
-
- var groupListener testGroupListener
- open_im_sdk.SetGroupListener(groupListener)
-
- InOutlllogin(uid, tk)
-}
-
-func InOutlllogin(uid, tk string) {
- var callback BaseSuccessFailed
- callback.time = time.Now()
- callback.funcName = utils.GetSelfFuncName()
- operationID := utils.OperationIDGenerator()
- open_im_sdk.Login(&callback, operationID, uid, tk)
- for {
- if callback.errCode == 1 {
- return
- } else if callback.errCode == -1 {
- time.Sleep(100 * time.Millisecond)
- } else {
- time.Sleep(100 * time.Millisecond)
- }
- }
-}
-
-func InOutLogout() {
- var callback BaseSuccessFailed
- callback.funcName = utils.GetSelfFuncName()
- opretaionID := utils.OperationIDGenerator()
- open_im_sdk.Logout(&callback, opretaionID)
-}
diff --git a/test/long_conn_test.go b/test/long_conn_test.go
new file mode 100644
index 000000000..8ad191920
--- /dev/null
+++ b/test/long_conn_test.go
@@ -0,0 +1,20 @@
+package test
+
+import (
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+ "testing"
+ "time"
+)
+
+func Test_SubscribeUsersStatus(t *testing.T) {
+ time.Sleep(time.Second)
+ message, err := open_im_sdk.UserForSDK.LongConnMgr().SubscribeUsersStatus(ctx, []string{"5975996883"})
+ if err != nil {
+ t.Error(err)
+ }
+ t.Log(message)
+ ch := make(chan struct{})
+ select {
+ case <-ch:
+ }
+}
diff --git a/test/online.go b/test/online.go
deleted file mode 100644
index 064a75ab7..000000000
--- a/test/online.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-//funcation OnlineTest(number int) {
-// t1 := time.Now()
-// RegisterOnlineAccounts(number)
-// log.Info("", "RegisterAccounts cost time: ", time.Since(t1), "Online client number ", number)
-// t2 := time.Now()
-// var wg sync.WaitGroup
-// wg.Add(number)
-// for i := 0; i < number; i++ {
-// go funcation(t int) {
-// GenWsConn(t)
-// log.Info("GenWsConn, the: ", t, " user")
-// wg.Done()
-// }(i)
-// }
-// wg.Wait()
-// log.Info("", "OnlineTest finish cost time: ", time.Since(t2), "Online client number ", number)
-//}
-
-//funcation GenWsConn(id int) {
-// userID := GenUid(id, "online")
-// token := RunGetToken(userID)
-// wsRespAsyn := interaction.NewWsRespAsyn()
-// wsConn := interaction.NewWsConn(new(testInitLister), token, userID, false)
-// cmdWsCh := make(chan common.Cmd2Value, 10)
-// pushMsgAndMaxSeqCh := make(chan common.Cmd2Value, 1000)
-// interaction.NewWs(wsRespAsyn, wsConn, cmdWsCh, pushMsgAndMaxSeqCh, nil)
-//}
diff --git a/test/reliability.go b/test/reliability.go
deleted file mode 100644
index 5ddb760bf..000000000
--- a/test/reliability.go
+++ /dev/null
@@ -1,687 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "errors"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/tools/log"
- "io/ioutil"
- "math/rand"
- "os"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-func GetFileContentAsStringLines(filePath string) ([]string, error) {
- result := []string{}
- b, err := ioutil.ReadFile(filePath)
- if err != nil {
- return result, err
- }
- s := string(b)
- for _, lineStr := range strings.Split(s, "\n") {
- lineStr = strings.TrimSpace(lineStr)
- if lineStr == "" {
- continue
- }
- result = append(result, lineStr)
- }
- return result, nil
-}
-
-func GetCmd(myUid int, filename string) int {
- cmd, err := GetFileContentAsStringLines("cmd.txt")
- if err != nil {
- fmt.Println("GetFileContentAsStringLines failed")
- return -1
- }
- if len(cmd) < myUid {
- fmt.Println("len failed")
- return -1
- }
- return int(utils.StringToInt64(cmd[myUid-1]))
-}
-
-func ReliabilityTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientNum int) {
- msgNumInOneClient = msgNumOneClient
- timeStamp := utils.Int64ToString(time.Now().Unix())
-
- var wg sync.WaitGroup
- // 注册
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- RegisterReliabilityUser(idx, timeStamp)
- wg.Done()
- }(i)
- }
- wg.Wait()
-
- log.ZWarn(ctx, "RegisterReliabilityUser finished, clientNum:", errors.New(""), "clientNum", clientNum)
- log.ZWarn(ctx, "init, login, send msg, start", errors.New(""))
-
- rand.Seed(time.Now().UnixNano())
-
- // 一半用户立刻登录发消息
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- rdSleep := rand.Intn(randSleepMaxSecond) + 1
- isSend := 0 // 消息是否成功发送控制量
- if isSend == 0 {
- go func(idx int) {
- log.ZWarn(ctx, " send msg flag true ", errors.New(""), "idx", idx)
- ReliabilityOne(idx, rdSleep, true, intervalSleepMS)
- wg.Done()
- }(i)
- sendMsgClient++
- } else {
- go func(idx int) {
- log.ZWarn(ctx, " send msg flag false ", errors.New(""), "idx", idx)
- ReliabilityOne(idx, rdSleep, false, intervalSleepMS)
- wg.Done()
- }(i)
- }
- }
- wg.Wait()
- log.ZWarn(ctx, "send msg finish, CheckReliabilityResult", errors.New(""))
-
- for {
- // 消息异步落库可能出现延迟,每隔五秒再检查一次
- if CheckReliabilityResult(msgNumOneClient, clientNum) {
- log.ZWarn(ctx, "CheckReliabilityResult ok, exit", errors.New(""))
- os.Exit(0)
- return
- } else {
- log.ZWarn(ctx, "CheckReliabilityResult failed , wait.... ", errors.New(""))
- }
- time.Sleep(time.Duration(5) * time.Second)
- }
-}
-
-func WorkGroupReliabilityTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientNum int, groupID string) {
- msgNumInOneClient = msgNumOneClient
- //timeStamp := utils.Int64ToString(time.Now().Unix())
-
- var wg sync.WaitGroup
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- WorkGroupRegisterReliabilityUser(idx)
- wg.Done()
- }(i)
- }
- wg.Wait()
-
- log.ZWarn(ctx, "RegisterReliabilityUser finished", errors.New(""), "clientNum", clientNum)
- log.ZWarn(ctx, " init, login, send msg, start ", errors.New(""))
- rand.Seed(time.Now().UnixNano())
-
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- rdSleep := rand.Intn(randSleepMaxSecond) + 1
- isSend := 0
- if isSend == 0 {
- go func(idx int) {
- log.ZWarn(ctx, "send msg flag true", errors.New(""), "idx", idx)
- WorkGroupReliabilityOne(idx, rdSleep, true, intervalSleepMS, groupID)
- wg.Done()
- }(i)
- sendMsgClient++
- } else {
- go func(idx int) {
- log.ZWarn(ctx, "send msg flag false", errors.New(""), "idx", idx)
- ReliabilityOne(idx, rdSleep, false, intervalSleepMS)
- wg.Done()
- }(i)
- }
- }
- wg.Wait()
- //log.Warn("send msg finish, CheckReliabilityResult")
-
- for {
- if CheckReliabilityResult(msgNumOneClient, clientNum) {
- //log.Warn("", "CheckReliabilityResult ok, exit")
- os.Exit(0)
- return
- } else {
- //log.Warn("", "CheckReliabilityResult failed , wait.... ")
- }
- time.Sleep(time.Duration(5) * time.Second)
- }
-}
-
-func WorkGroupMsgDelayTest(msgNumOneClient int, intervalSleepMS int, randSleepMaxSecond int, clientBegin int, clientEnd int, groupID string) {
- msgNumInOneClient = msgNumOneClient
-
- var wg sync.WaitGroup
-
- wg.Add(clientEnd - clientBegin + 1)
- for i := clientBegin; i <= clientEnd; i++ {
- go func(idx int) {
- WorkGroupRegisterReliabilityUser(idx)
- wg.Done()
- }(i)
- }
- wg.Wait()
-
- //log.Warn("", "RegisterReliabilityUser finished, client: ", clientBegin, clientEnd)
- //log.Warn("", " init, login, send msg, start ")
- rand.Seed(time.Now().UnixNano())
-
- wg.Add(clientEnd - clientBegin + 1)
- for i := clientBegin; i <= clientEnd; i++ {
- rdSleep := rand.Intn(randSleepMaxSecond) + 1
- isSend := 0
- if isSend == 0 {
- go func(idx int) {
- //log.Warn("", " send msg flag true ", idx)
- WorkGroupReliabilityOne(idx, rdSleep, true, intervalSleepMS, groupID)
- wg.Done()
- }(i)
- sendMsgClient++
- } else {
- go func(idx int) {
- //log.Warn("", " send msg flag false ", idx)
- WorkGroupReliabilityOne(idx, rdSleep, false, intervalSleepMS, groupID)
- wg.Done()
- }(i)
- }
- }
- wg.Wait()
- //log.Warn("send msg finish, CheckReliabilityResult")
-
- for {
- if CheckReliabilityResult(msgNumOneClient, clientEnd-clientBegin+1) {
- //log.Warn("", "CheckReliabilityResult ok, exit")
- os.Exit(0)
- return
- } else {
- //log.Warn("", "CheckReliabilityResult failed , wait.... ")
- }
- time.Sleep(time.Duration(5) * time.Second)
- }
-}
-
-func PressTest(msgNumOneClient int, intervalSleepMS int, clientNum int) {
- msgNumInOneClient = msgNumOneClient
- //timeStamp := utils.Int64ToString(time.Now().Unix())
- var wg sync.WaitGroup
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- RegisterPressUser(idx)
- log.ZInfo(ctx, "GetUserTokenFinish", "index", idx)
-
- wg.Done()
- }(i)
- }
- wg.Wait()
- //log.Warn("", "get all user token finish ", clientNum, " cost time: ", time.Since(t1))
-
- //log.Warn("", "init and login begin ")
-
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- strMyUid := allLoginMgr[idx].userID
- token := allLoginMgr[idx].token
- PressInitAndLogin(idx, strMyUid, token, WSADDR, APIADDR)
- wg.Done()
- }(i)
- }
- wg.Wait()
- //log.Warn("", "init and login end ", " cost time: ", time.Since(t1))
-
- //log.Warn("", "send msg begin ")
-
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- PressOne(idx, 0, true, intervalSleepMS)
- //log.Warn("", "press finished ", idx)
- wg.Done()
- }(i)
- }
- wg.Wait()
- sendMsgTotalSuccessNum := uint32(0)
- sendMsgTotalFailedNum := uint32(0)
- for _, v := range allLoginMgr {
- sendMsgTotalSuccessNum += v.sendMsgSuccessNum
- sendMsgTotalFailedNum += v.sendMsgFailedNum
- }
- //log.Warn("send msg end ", "number of messages expected to be sent: ", clientNum*msgNumOneClient, " sendMsgTotalSuccessNum: ", sendMsgTotalSuccessNum, " sendMsgTotalFailedNum: ", sendMsgTotalFailedNum, "cost time: ", time.Since(t1))
-}
-
-func WorkGroupPressTest(msgNumOneClient int, intervalSleepMS int, clientNum int, groupID string) {
- msgNumInOneClient = msgNumOneClient
- var wg sync.WaitGroup
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- WorkGroupRegisterReliabilityUser(idx)
- log.ZInfo(ctx, "GetUserTokenFinish", "index", idx)
-
- wg.Done()
- }(i)
- }
- wg.Wait()
- //log.Warn("", "get all user token finish ", clientNum, " cost time: ", time.Since(t1))
-
- //log.Warn("", "init and login begin ")
-
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- strMyUid := allLoginMgr[idx].userID
- token := allLoginMgr[idx].token
- ReliabilityInitAndLogin(idx, strMyUid, token, WSADDR, APIADDR)
- wg.Done()
- }(i)
- }
- wg.Wait()
- //log.Warn("", "init and login end ", " cost time: ", time.Since(t1))
-
- //log.Warn("", "send msg begin ")
-
- wg.Add(clientNum)
- for i := 0; i < clientNum; i++ {
- go func(idx int) {
- WorkGroupPressOne(idx, 0, true, intervalSleepMS, groupID)
- wg.Done()
- }(i)
- }
- wg.Wait()
- sendMsgTotalSuccessNum := uint32(0)
- sendMsgTotalFailedNum := uint32(0)
- for _, v := range allLoginMgr {
- sendMsgTotalSuccessNum += v.sendMsgSuccessNum
- sendMsgTotalFailedNum += v.sendMsgFailedNum
- }
- //log.Warn("send msg end ", "number of messages expected to be sent: ", clientNum*msgNumOneClient, " sendMsgTotalSuccessNum: ", sendMsgTotalSuccessNum, " sendMsgTotalFailedNum: ", sendMsgTotalFailedNum, "cost time: ", time.Since(t1))
-}
-
-func CheckReliabilityResult(msgNumOneClient int, clientNum int) bool {
- log.ZInfo(ctx, "StartCheckMapSendToMapRecv")
-
- sameNum := 0
-
- // 消息数量不一致说明出现丢失
- if len(SendSuccAllMsg)+len(SendFailedAllMsg) != msgNumOneClient*clientNum {
- //log.Warn("", utils.GetSelfFuncName(), " send msg success number: ", len(SendSuccAllMsg),
- // " send msg failed number: ", len(SendFailedAllMsg), " all: ", msgNumOneClient*clientNum)
- return false
- }
-
- for ksend, _ := range SendSuccAllMsg {
- _, ok := RecvAllMsg[ksend] // RecvAllMsg 的初始化何时?
- if ok {
- sameNum++
- } else {
- // 埋点日志,第 ksend 个消息数据 本地和服务器不一致
- //log.Error("", "check failed not in recv ", ksend)
- //log.Error("", "send failed num: ", len(SendFailedAllMsg),
- // " send success num: ", len(SendSuccAllMsg), " recv num: ", len(RecvAllMsg))
- return false
- }
- }
- log.ZInfo(ctx, "CheckMapSendToMapRecvOK", "sameNum", sameNum)
-
- //log.Info("", "start check map recv -> map send ")
- //sameNum = 0
-
- //for k1, _ := range RecvAllMsg {
- // _, ok := SendSuccAllMsg[k1]
- // if ok {
- // sameNum++
- // //x := v1 + v2
- // //x = x + x
- //
- // } else {
- // log.Error("", "check failed not in send ", k1, len(SendFailedAllMsg), len(SendSuccAllMsg), len(RecvAllMsg))
- // // return false
- // }
- //}
- minCostTime := int64(1000000)
- maxCostTime := int64(0)
- totalCostTime := int64(0)
- for ksend, vsend := range SendSuccAllMsg {
- krecv, ok := RecvAllMsg[ksend]
- if ok {
- sameNum++
- costTime := krecv.RecvTime - vsend.SendTime
- totalCostTime += costTime
- if costTime > maxCostTime {
-
- maxCostTime = costTime
- }
- if minCostTime > costTime {
- minCostTime = costTime
- }
- }
- }
-
- //log.Warn("", "need send msg num : ", sendMsgClient*msgNumInOneClient)
- //log.Warn("", "send msg succ num ", len(SendSuccAllMsg))
- //log.Warn("", "send msg failed num ", len(SendFailedAllMsg))
- //log.Warn("", "recv msg succ num ", len(RecvAllMsg))
- //log.Warn("", "minCostTime: ", minCostTime, "ms, maxCostTime: ", maxCostTime, "ms, average cost time: ",
- // totalCostTime/(int64(sendMsgClient*msgNumInOneClient)), "ms", " maxCostMsgID: ", maxCostMsgID)
-
- return true
-}
-
-func ReliabilityOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int) {
- // time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
- strMyUid := allLoginMgr[index].userID
- token := allLoginMgr[index].token
- log.ZInfo(ctx, "LoginOKClientNum", "clientNum", len(allLoginMgr), "userID", strMyUid, "token", token, "index", index)
-
- ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
-
- //log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
-
- msgnum := msgNumInOneClient
- uidNum := len(allLoginMgr)
- rand.Seed(time.Now().UnixNano())
- if msgnum == 0 {
- os.Exit(0)
- }
- if !isSendMsg {
- // Msgwg.Done()
- } else {
- for i := 0; i < msgnum; i++ {
- var r int
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- for {
- r = rand.Intn(uidNum)
- if r == index {
- continue
- } else {
- break
- }
- }
- recvId := allLoginMgr[r].userID
- idx := strconv.FormatInt(int64(i), 10)
- for {
- if runtime.NumGoroutine() > MaxNumGoroutine {
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //log.Warn("", "NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
- continue
- } else {
- break
- }
- }
- DoTestSendMsg(index, strMyUid, recvId, "", idx)
- }
- //Msgwg.Done()
- }
-}
-
-func WorkGroupReliabilityOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
- // time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
- strMyUid := allLoginMgr[index].userID
- token := allLoginMgr[index].token
- ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
- log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
-
- //log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
- msgnum := msgNumInOneClient
- uidNum := len(allLoginMgr)
- var idx string
- rand.Seed(time.Now().UnixNano())
- if msgnum == 0 {
- os.Exit(0)
- }
- if !isSendMsg {
- // Msgwg.Done()
- } else {
- for i := 0; i < msgnum; i++ {
- var r int
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- for {
- r = rand.Intn(uidNum)
- if r == index {
- continue
- } else {
-
- break
- }
-
- }
-
- idx = strconv.FormatInt(int64(i), 10)
- for {
- if runtime.NumGoroutine() > MaxNumGoroutine {
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //log.Warn("", "NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
- continue
- } else {
- break
- }
- }
-
- DoTestSendMsg(index, strMyUid, "", groupID, idx)
-
- }
- //Msgwg.Done()
- }
-}
-
-func WorkGroupMsgDelayOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
- // time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
- strMyUid := allLoginMgr[index].userID
- token := allLoginMgr[index].token
- ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
- log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
-
- //log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
- msgnum := msgNumInOneClient
- uidNum := len(allLoginMgr)
- var idx string
- rand.Seed(time.Now().UnixNano())
- if msgnum == 0 {
- os.Exit(0)
- }
- if !isSendMsg {
- // Msgwg.Done()
- } else {
- for i := 0; i < msgnum; i++ {
- var r int
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- for {
- r = rand.Intn(uidNum)
- if r == index {
- continue
- } else {
-
- break
- }
-
- }
-
- idx = strconv.FormatInt(int64(i), 10)
- for {
- if runtime.NumGoroutine() > MaxNumGoroutine {
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //log.Warn("", "NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
- continue
- } else {
- break
- }
- }
-
- DoTestSendMsg(index, strMyUid, "", groupID, idx)
-
- }
- //Msgwg.Done()
- }
-}
-
-//
-//funcation WorkGroupMsgDelayOne(sendID1 string, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
-// // time.Sleep(time.Duration(beforeLoginSleep) * time.Second)
-// strMyUid := allLoginMgr[index].userID
-// token := allLoginMgr[index].token
-// ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
-// log.Info("", "login ok client num: ", len(allLoginMgr))
-// log.Warn("start One", index, beforeLoginSleep, isSendMsg, strMyUid, token, WSADDR, APIADDR)
-// msgnum := msgNumInOneClient
-// uidNum := len(allLoginMgr)
-// var idx string
-// rand.Seed(time.Now().UnixNano())
-// if msgnum == 0 {
-// os.Exit(0)
-// }
-// if !isSendMsg {
-// // Msgwg.Done()
-// } else {
-// for i := 0; i < msgnum; i++ {
-// var r int
-// time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
-// for {
-// r = rand.Intn(uidNum)
-// if r == index {
-// continue
-// } else {
-//
-// break
-// }
-//
-// }
-//
-// idx = strconv.FormatInt(int64(i), 10)
-// for {
-// if runtime.NumGoroutine() > MaxNumGoroutine {
-// time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
-// log.Warn("", "NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
-// continue
-// } else {
-// break
-// }
-// }
-//
-// DoTestSendMsg(index, strMyUid, "", groupID, idx)
-//
-// }
-// //Msgwg.Done()
-// }
-//}
-
-func PressOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int) {
- if beforeLoginSleep != 0 {
- time.Sleep(time.Duration(beforeLoginSleep) * time.Millisecond)
- }
-
- strMyUid := allLoginMgr[index].userID
- token := allLoginMgr[index].token
- // ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
- log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
- log.ZInfo(ctx, "StartOne", "index", index, "beforeLoginSleep", beforeLoginSleep, "isSendMsg", isSendMsg, "senderUID", strMyUid, "token", token, "wsAddr", WSADDR, "apiAddr", APIADDR)
-
- msgnum := msgNumInOneClient
- uidNum := len(allLoginMgr)
- var recvId string
- var idx string
- rand.Seed(time.Now().UnixNano())
- if msgnum == 0 {
- os.Exit(0)
- }
- if !isSendMsg {
- // Msgwg.Done()
- } else {
- for i := 0; i < msgnum; i++ {
- var r int
- // time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- for {
- r = rand.Intn(uidNum)
- if r == index {
- continue
- } else {
-
- break
- }
-
- }
-
- recvId = allLoginMgr[r].userID
- idx = strconv.FormatInt(int64(i), 10)
- for {
- if runtime.NumGoroutine() > MaxNumGoroutine {
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //log.Warn("", " NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
- continue
- } else {
- break
- }
- }
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //DoTestSendMsg(index, strMyUid, recvId, idx)
- if sendPressMsg(index, strMyUid, recvId, "", idx) {
- allLoginMgr[index].sendMsgSuccessNum++
- } else {
- allLoginMgr[index].sendMsgFailedNum++
- }
- }
- //Msgwg.Done()
- }
-}
-
-func WorkGroupPressOne(index int, beforeLoginSleep int, isSendMsg bool, intervalSleepMS int, groupID string) {
- if beforeLoginSleep != 0 {
- time.Sleep(time.Duration(beforeLoginSleep) * time.Millisecond)
- }
- strMyUid := allLoginMgr[index].userID
- token := allLoginMgr[index].token
- //ReliabilityInitAndLogin(index, strMyUid, token, WSADDR, APIADDR)
- log.ZInfo(ctx, "LoginSuccess", "clientNum", len(allLoginMgr))
- log.ZInfo(ctx, "StartOne", "index", index, "beforeLoginSleep", beforeLoginSleep, "isSendMsg", isSendMsg, "senderUID", strMyUid, "token", token, "wsAddr", WSADDR, "apiAddr", APIADDR)
-
- msgnum := msgNumInOneClient
- var idx string
- rand.Seed(time.Now().UnixNano())
- if msgnum == 0 {
- os.Exit(0)
- }
- if !isSendMsg {
- } else {
- for i := 0; i < msgnum; i++ {
- idx = strconv.FormatInt(int64(i), 10)
-
- for {
- if runtime.NumGoroutine() > MaxNumGoroutine {
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- //log.Warn("", " NumGoroutine > max ", runtime.NumGoroutine(), MaxNumGoroutine)
- continue
- } else {
- break
- }
- }
- log.ZInfo(ctx, "SendPressMsgBegin", "index", index, "senderUID", strMyUid, "groupID", groupID)
-
- if sendPressMsg(index, strMyUid, "", groupID, idx) {
- allLoginMgr[index].sendMsgSuccessNum++
- } else {
- allLoginMgr[index].sendMsgFailedNum++
- }
- log.ZInfo(ctx, "sendPressMsg end")
- time.Sleep(time.Duration(intervalSleepMS) * time.Millisecond)
- }
- }
-}
diff --git a/test/sendMessage.go b/test/sendMessage.go
deleted file mode 100644
index 7472c7305..000000000
--- a/test/sendMessage.go
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
-)
-
-func init() {
- //sdk_struct.SvrConf = sdk_struct.IMConfig{Platform: 1, ApiAddr: APIADDR, WsAddr: WSADDR, DataDir: "./", LogLevel: 6, ObjectStorage: "cos"}
- allLoginMgr = make(map[int]*CoreNode)
-
-}
-
-//funcation InitMgr(num int) {
-// log.Warn("", "allLoginMgr cap: ", num)
-// allLoginMgr = make(map[int]*CoreNode, num)
-//}
-
-type CoreNode struct {
- token string
- userID string
- mgr *open_im_sdk.LoginMgr
- sendMsgSuccessNum uint32
- sendMsgFailedNum uint32
- idx int
-}
-
-func addSendSuccess() {
- sendSuccessLock.Lock()
- defer sendSuccessLock.Unlock()
- sendSuccessCount++
-}
-func addSendFailed() {
- sendFailedLock.Lock()
- defer sendFailedLock.Unlock()
- sendFailedCount++
-}
-
-//
-//funcation TestSendCostTime() {
-// GenWsConn(0)
-// sendID := allUserID[0]
-// recvID := allUserID[0]
-// for {
-// operationID := utils.OperationIDGenerator()
-// b := SendTextMessage("test", sendID, recvID, operationID, allWs[0])
-// if b {
-// log.Debug(operationID, sendID, recvID, "SendTextMessage success")
-// } else {
-// log.Error(operationID, sendID, recvID, "SendTextMessage failed")
-// }
-// time.Sleep(time.Duration(5) * time.Second)
-// log.Debug(operationID, "//////////////////////////////////")
-// }
-//
-//}
-//funcation TestSend(idx int, text string, uidNum, intervalSleep int) {
-// for {
-// operationID := utils.OperationIDGenerator()
-// sendID := allUserID[idx]
-// recvID := allUserID[rand.Intn(uidNum)]
-// b := SendTextMessage(text, sendID, recvID, operationID, allWs[idx])
-// if b {
-// log.Debug(operationID, sendID, recvID, "SendTextMessage success")
-// } else {
-// log.Error(operationID, sendID, recvID, "SendTextMessage failed")
-// }
-// time.Sleep(time.Duration(rand.Intn(intervalSleep)) * time.Millisecond)
-// }
-//}
-//
-
-//funcation sendPressMsg(idx int, text string, uidNum, intervalSleep int) {
-// for {
-// operationID := utils.OperationIDGenerator()
-// sendID := allUserID[idx]
-// recvID := allUserID[rand.Intn(uidNum)]
-// b := SendTextMessageOnlyForPress(text, sendID, recvID, operationID, allLoginMgr[idx].mgr.Ws())
-// if b {
-// log.Debug(operationID, sendID, recvID, "SendTextMessage success")
-// } else {
-// log.Error(operationID, sendID, recvID, "SendTextMessage failed ")
-// }
-// time.Sleep(time.Duration(rand.Intn(intervalSleep)) * time.Second)
-// }
-//}
-
-func sendPressMsg(index int, sendId, recvID string, groupID string, idx string) bool {
-
- return SendTextMessageOnlyForPress(idx, sendId, recvID, groupID, utils.OperationIDGenerator())
-}
-func SendTextMessageOnlyForPress(text, senderID, recvID, groupID, operationID string) bool {
- var wsMsgData sdkws.MsgData
- options := make(map[string]bool, 2)
- wsMsgData.SendID = senderID
- if groupID == "" {
- wsMsgData.RecvID = recvID
- wsMsgData.SessionType = constant.SingleChatType
- } else {
- wsMsgData.GroupID = groupID
- wsMsgData.SessionType = constant.SuperGroupChatType
- }
-
- wsMsgData.ClientMsgID = utils.GetMsgID(senderID)
- wsMsgData.SenderPlatformID = 1
-
- wsMsgData.MsgFrom = constant.UserMsgType
- wsMsgData.ContentType = constant.Text
- wsMsgData.Content = []byte(text)
- wsMsgData.CreateTime = utils.GetCurrentTimestampByMill()
- wsMsgData.Options = options
- wsMsgData.OfflinePushInfo = nil
- //timeout := 300
- log.ZInfo(ctx, "SendReqTest begin ", "operationID", operationID, "wsMsgData", wsMsgData)
- //flag := ws.SendReqTest(&wsMsgData, constant.WSSendMsg, timeout, senderID, operationID)
- //
- //if flag != true {
- // log.Warn(operationID, "SendReqTest failed ", wsMsgData)
- //}
- return true
-}
diff --git a/test/t_conversation_msg.go b/test/t_conversation_msg.go
deleted file mode 100644
index ae3dabff6..000000000
--- a/test/t_conversation_msg.go
+++ /dev/null
@@ -1,927 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "encoding/json"
- "fmt"
- "sync"
-
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/log"
-
- "github.com/openimsdk/protocol/sdkws"
-
- "github.com/openimsdk/tools/mcontext"
-)
-
-//funcation DotestSetConversationRecvMessageOpt() {
-// var callback BaseSuccessFailedTest
-// callback.funcName = utils.GetSelfFuncName()
-// var idList []string
-// idList = append(idList, "18567155635")
-// jsontest, _ := json.Marshal(idList)
-// open_im_sdk.SetConversationRecvMessageOpt(&callback, string(jsontest), 2)
-// fmt.Println("SetConversationRecvMessageOpt", string(jsontest))
-//}
-//
-//funcation DoTestGetMultipleConversation() {
-// var callback BaseSuccessFailedTest
-// callback.funcName = utils.GetSelfFuncName()
-// var idList []string
-// fmt.Println("DoTestGetMultipleConversation come here")
-// idList = append(idList, "single_13977954313", "group_77215e1b13b75f3ab00cb6570e3d9618")
-// jsontest, _ := json.Marshal(idList)
-// open_im_sdk.GetMultipleConversation(string(jsontest), &callback)
-// fmt.Println("GetMultipleConversation", string(jsontest))
-//}
-//
-//funcation DoTestGetConversationRecvMessageOpt() {
-// var callback BaseSuccessFailedTest
-// callback.funcName = utils.GetSelfFuncName()
-// var idList []string
-// idList = append(idList, "18567155635")
-// jsontest, _ := json.Marshal(idList)
-// open_im_sdk.GetConversationRecvMessageOpt(&callback, string(jsontest))
-// fmt.Println("GetConversationRecvMessageOpt", string(jsontest))
-//}
-
-func DoTestDeleteAllMsgFromLocalAndSvr() {
- var deleteConversationCallback DeleteConversationCallBack
- operationID := utils.OperationIDGenerator()
- log.ZInfo(ctx, operationID, utils.GetSelfFuncName(), "args")
- open_im_sdk.DeleteAllMsgFromLocalAndSvr(deleteConversationCallback, operationID)
-}
-func DoTestSearchLocalMessages() {
- //[SearchLocalMessages args: {"conversationID":"single_707010937","keywordList":["1"],"keywordListMatchType":0,"senderUserIDList":[],"messageTypeList":[],"searchTimePosition":0,"searchTimePeriod":0,"pageIndex":1,"count":200}]
- var testSearchLocalMessagesCallBack SearchLocalMessagesCallBack
- testSearchLocalMessagesCallBack.OperationID = utils.OperationIDGenerator()
- var params sdk_params_callback.SearchLocalMessagesParams
- params.KeywordList = []string{"1"}
- params.ConversationID = "super_group_3907826375"
- params.Count = 20
- params.PageIndex = 1
- //s:=strings.Trim(params.KeywordList[0],"")
- //fmt.Println(len(s),s)
- //params.KeywordListMatchType = 1
- params.MessageTypeList = []int{101, 106}
- open_im_sdk.SearchLocalMessages(testSearchLocalMessagesCallBack, testSearchLocalMessagesCallBack.OperationID, utils.StructToJsonString(params))
-}
-
-// funcation DoTestGetHistoryMessage(userID string) {
-// var testGetHistoryCallBack GetHistoryCallBack
-// testGetHistoryCallBack.OperationID = utils.OperationIDGenerator()
-// var params sdk_params_callback.GetHistoryMessageListParams
-// params.UserID = userID
-// params.ConversationID = "super_group_3907826375"
-// //params.StartClientMsgID = "97f12899778823019f13ea46b0c1e6dd"
-// params.Count = 10
-// open_im_sdk.GetHistoryMessageList(testGetHistoryCallBack, testGetHistoryCallBack.OperationID, utils.StructToJsonString(params))
-// }
-func DoTestFindMessageList() {
- var testFindMessageListCallBack FindMessageListCallBack
- testFindMessageListCallBack.OperationID = utils.OperationIDGenerator()
- var params sdk_params_callback.FindMessageListParams
- temp := sdk_params_callback.ConversationArgs{ConversationID: "super_group_4205679980", ClientMsgIDList: []string{"eee68d85a43991d6b2e7354c52c5321d", "736f40f902046a6e879dc7257d3e81df"}}
- //temp1 := sdk_params_callback.ConversationArgs{ConversationID: "super_group_3320742908", ClientMsgIDList: []string{"acf09fcdda48bf2cb39faba31ac63b5c", "b121d3a7f269636afd255b6001d3fc80", "d8951d1c5192ad39f37f44de93a83302"}}
- params = append(params, &temp)
- //params = append(params, &temp1)
- open_im_sdk.FindMessageList(testFindMessageListCallBack, testFindMessageListCallBack.OperationID, utils.StructToJsonString(params))
-}
-func DoTestSetMessageReactionExtensions() {
- var testSetMessageReactionExtensionsCallBack SetMessageReactionExtensionsCallBack
- testSetMessageReactionExtensionsCallBack.OperationID = utils.OperationIDGenerator()
- var params sdk_params_callback.SetMessageReactionExtensionsParams
- var data server_api_params.KeyValue
- data.TypeKey = "x"
- m := make(map[string]interface{})
- m["operation"] = "1"
- m["operator"] = "1583984945064968192"
- data.Value = utils.StructToJsonString(m)
- params = append(params, &data)
- s := sdk_struct.MsgStruct{}
- s.SessionType = 3
- s.GroupID = "1420026997"
- s.ClientMsgID = "831c270ae1d7472dc633e7be06b37db5"
- //params = append(params, &temp1)
- // open_im_sdk.SetMessageReactionExtensions(testSetMessageReactionExtensionsCallBack, testSetMessageReactionExtensionsCallBack.OperationID, utils.StructToJsonString(s),
- // utils.StructToJsonString(params))
-}
-func DoTestAddMessageReactionExtensions(index int, operationID string) {
- var testAddMessageReactionExtensionsCallBack AddMessageReactionExtensionsCallBack
- testAddMessageReactionExtensionsCallBack.OperationID = operationID
- fmt.Printf("DoTestAddMessageReactionExtensions opid:", testAddMessageReactionExtensionsCallBack.OperationID, index)
- var params sdk_params_callback.AddMessageReactionExtensionsParams
- var data server_api_params.KeyValue
- data.TypeKey = "x"
- m := make(map[string]interface{})
- m["operation"] = index
- m["operator"] = "1583984945064968192"
- data.Value = utils.StructToJsonString(m)
- params = append(params, &data)
- s := sdk_struct.MsgStruct{}
- s.SessionType = 3
- s.GroupID = "1623878302774460418"
- s.ClientMsgID = "7ca152a836a0f784c07a3b74d4e2a97d"
- //params = append(params, &temp1)
- // open_im_sdk.AddMessageReactionExtensions(testAddMessageReactionExtensionsCallBack, testAddMessageReactionExtensionsCallBack.OperationID, utils.StructToJsonString(s),
- // utils.StructToJsonString(params))
-}
-func DoTestGetMessageListReactionExtensions(operationID string) {
- var testGetMessageReactionExtensionsCallBack GetMessageListReactionExtensionsCallBack
- testGetMessageReactionExtensionsCallBack.OperationID = operationID
- fmt.Printf("DoTestGetMessageListReactionExtensions opid:", testGetMessageReactionExtensionsCallBack.OperationID)
- var ss []sdk_struct.MsgStruct
- s := sdk_struct.MsgStruct{}
- s.SessionType = 3
- s.GroupID = "1623878302774460418"
- s.ClientMsgID = "d91943a8085556853b3457e33d1e21b2"
- s1 := sdk_struct.MsgStruct{}
- s1.SessionType = 3
- s1.GroupID = "1623878302774460418"
- s1.ClientMsgID = "7ca152a836a0f784c07a3b74d4e2a97d"
- ss = append(ss, s)
- ss = append(ss, s1)
- //params = append(params, &temp1)
- // open_im_sdk.GetMessageListReactionExtensions(testGetMessageReactionExtensionsCallBack, testGetMessageReactionExtensionsCallBack.OperationID, utils.StructToJsonString(ss))
-}
-func DoTestSetAppBadge() {
- var testSetAppBadgeCallBack SetAppBadgeCallBack
- testSetAppBadgeCallBack.OperationID = utils.OperationIDGenerator()
- open_im_sdk.SetAppBadge(testSetAppBadgeCallBack, testSetAppBadgeCallBack.OperationID, 100)
-}
-
-func DoTestGetAdvancedHistoryMessageList() {
- var testGetHistoryCallBack GetHistoryCallBack
- testGetHistoryCallBack.OperationID = utils.OperationIDGenerator()
- var params sdk_params_callback.GetAdvancedHistoryMessageListParams
- params.ConversationID = "si_7788_7789"
- //params.StartClientMsgID = "83ca933d559d0374258550dd656a661c"
- params.Count = 20
- //params.LastMinSeq = seq
- open_im_sdk.GetAdvancedHistoryMessageList(testGetHistoryCallBack, testGetHistoryCallBack.OperationID, utils.StructToJsonString(params))
-}
-
-//funcation DoTestGetHistoryMessageReverse(userID string) {
-// var testGetHistoryReverseCallBack GetHistoryReverseCallBack
-// testGetHistoryReverseCallBack.OperationID = utils.OperationIDGenerator()
-// var params sdk_params_callback.GetHistoryMessageListParams
-// params.UserID = userID
-// params.Count = 10
-// params.ConversationID = "single_707008149"
-// params.StartClientMsgID = "d40dde77f29b14d3a16ca6f422776890"
-// open_im_sdk.GetHistoryMessageListReverse(testGetHistoryReverseCallBack, testGetHistoryReverseCallBack.OperationID, utils.StructToJsonString(params))
-//}
-//funcation DoTestGetGroupHistoryMessage() {
-// var testGetHistoryCallBack GetHistoryCallBack
-// testGetHistoryCallBack.OperationID = utils.OperationIDGenerator()
-// var params sdk_params_callback.GetHistoryMessageListParams
-// params.GroupID = "cb7aaa8e5f83d92db2ed1573cd01870c"
-// params.Count = 10
-// open_im_sdk.GetHistoryMessageList(testGetHistoryCallBack, testGetHistoryCallBack.OperationID, utils.StructToJsonString(params))
-//}
-
-//funcation DoTestGetGroupHistoryMessage() {
-// var testGetHistoryCallBack GetHistoryCallBack
-// testGetHistoryCallBack.OperationID = utils.OperationIDGenerator()
-// var params sdk_params_callback.GetHistoryMessageListParams
-// params.GroupID = "cb7aaa8e5f83d92db2ed1573cd01870c"
-// params.Count = 10
-// open_im_sdk.GetHistoryMessageList(testGetHistoryCallBack, testGetHistoryCallBack.OperationID, utils.StructToJsonString(params))
-//}
-
-//funcation DoTestDeleteConversation(conversationID string) {
-// var testDeleteConversation DeleteConversationCallBack
-// open_im_sdk.DeleteConversation(conversationID, testDeleteConversation)
-//
-//}
-
-type DeleteConversationCallBack struct {
-}
-
-func (d DeleteConversationCallBack) OnError(errCode int32, errMsg string) {
- fmt.Printf("DeleteConversationCallBack , errCode:%v,errMsg:%v\n", errCode, errMsg)
-}
-
-func (d DeleteConversationCallBack) OnSuccess(data string) {
- fmt.Printf("DeleteConversationCallBack , success,data:%v\n", data)
-}
-
-type DeleteMessageCallBack struct {
- Msg string
-}
-
-func (d DeleteMessageCallBack) OnError(errCode int32, errMsg string) {
- fmt.Printf("DeleteMessageCallBack , errCode:%v,errMsg:%v\n", errCode, errMsg)
-}
-
-func (d *DeleteMessageCallBack) OnSuccess(data string) {
- fmt.Printf("DeleteMessageCallBack , success,data:%v\n", data)
- d.Msg = data
-}
-
-func (d DeleteMessageCallBack) GetMessage() string {
- return d.Msg
-}
-
-func DoTestDeleteConversationMsgFromLocalAndSvr(conversationID string) {
- cb := &DeleteMessageCallBack{}
- operationID := utils.OperationIDGenerator()
- open_im_sdk.DeleteConversationAndDeleteAllMsg(cb, operationID, conversationID)
-}
-
-type TestGetAllConversationListCallBack struct {
- OperationID string
-}
-
-func (t TestGetAllConversationListCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestGetAllConversationListCallBack ", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t TestGetAllConversationListCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "ConversationCallBack ", "operationID", t.OperationID, "data", data)
-}
-
-func DoTestGetAllConversation() {
- var test TestGetAllConversationListCallBack
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetAllConversationList(test, test.OperationID)
-}
-
-func DoTestGetOneConversation(friendID string) {
- var test TestGetAllConversationListCallBack
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetOneConversation(test, test.OperationID, constant.SingleChatType, friendID)
-}
-
-func DoTestGetConversations(conversationIDs string) {
- var test TestGetAllConversationListCallBack
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetMultipleConversation(test, test.OperationID, conversationIDs)
-}
-
-type TestSetConversationPinnedCallback struct {
- OperationID string
-}
-
-func (t TestSetConversationPinnedCallback) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestSetConversationPinnedCallback ", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t TestSetConversationPinnedCallback) OnSuccess(data string) {
- log.ZInfo(ctx, "TestSetConversationPinnedCallback ", "operationID", t.OperationID, "data", data)
-}
-
-func DoTestSetConversationRecvMessageOpt(conversationIDs []string, opt int) {
- var callback testProcessGroupApplication
- callback.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DoTestSetConversationRecvMessageOpt", "operationID", callback.OperationID, "funcName", utils.GetSelfFuncName(),
- "conversationIDs: ", conversationIDs, "opt", opt)
- s := utils.StructToJsonString(conversationIDs)
- open_im_sdk.SetConversationRecvMessageOpt(callback, callback.OperationID, s, opt)
-}
-
-//func DoTestRevoke() {
-// var callback testProcessGroupApplication
-// open_im_sdk.RevokeMessage(callback, "si_3232515230_8650796072", utils.StructToJsonString(&sdk_struct.MsgStruct{SessionType: 1, ContentType: 101,
-// ClientMsgID: "ebfe4e0aa11e7602de3dfe0670b484cd", Seq: 12, SendID: "8650796072", RecvID: "3232515230"}))
-//}
-
-func DoTestClearOneConversation() {
- var callback testProcessGroupApplication
- open_im_sdk.ClearConversationAndDeleteAllMsg(callback, "df", "si_2456093263_9169012630")
-}
-
-func DoTestSetConversationPinned(conversationID string, pin bool) {
- var test TestSetConversationPinnedCallback
- test.OperationID = "testping"
- open_im_sdk.PinConversation(test, test.OperationID, conversationID, pin)
-}
-
-func DoTestSetOneConversationRecvMessageOpt(conversationID string, opt int) {
- var test TestSetConversationPinnedCallback
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.SetConversationRecvMessageOpt(test, test.OperationID, conversationID, opt)
-}
-
-func DoTestSetOneConversationPrivateChat(conversationID string, privateChat bool) {
- var test TestSetConversationPinnedCallback
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.SetConversationPrivateChat(test, test.OperationID, conversationID, privateChat)
-}
-
-func DoTestSetBurnDuration(conversationID string) {
- var test TestSetConversationPinnedCallback
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.SetConversationBurnDuration(test, test.OperationID, conversationID, 300)
-}
-
-func DoTestSetMsgDestructTime(conversationID string) {
- var test TestSetConversationPinnedCallback
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.SetConversationIsMsgDestruct(test, test.OperationID, conversationID, true)
- open_im_sdk.SetConversationMsgDestructTime(test, test.OperationID, conversationID, 300010)
-}
-
-type TestGetConversationListSplitCallBack struct {
- OperationID string
-}
-
-func (t TestGetConversationListSplitCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestGetConversationListSplitCallBack err ", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t TestGetConversationListSplitCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "TestGetConversationListSplitCallBack success", "operationID", t.OperationID, "data", data)
-}
-func DoTestGetConversationListSplit() {
- var test TestGetConversationListSplitCallBack
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetConversationListSplit(test, test.OperationID, 1, 2)
-}
-
-type TestGetOneConversationCallBack struct {
-}
-
-func (t TestGetOneConversationCallBack) OnError(errCode int32, errMsg string) {
- fmt.Printf("TestGetOneConversationCallBack , errCode:%v,errMsg:%v\n", errCode, errMsg)
-}
-
-func (t TestGetOneConversationCallBack) OnSuccess(data string) {
- fmt.Printf("TestGetOneConversationCallBack , success,data:%v\n", data)
-}
-
-func DoTestGetConversationRecvMessageOpt(list string) {
- var test TestGetConversationRecvMessageOpt
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetConversationRecvMessageOpt(test, test.OperationID, list)
-}
-
-type TestGetConversationRecvMessageOpt struct {
- OperationID string
-}
-
-func (t TestGetConversationRecvMessageOpt) OnError(errCode int32, errMsg string) {
- fmt.Printf("TestGetConversationRecvMessageOpt , errCode:%v,errMsg:%v\n", errCode, errMsg)
-}
-
-func (t TestGetConversationRecvMessageOpt) OnSuccess(data string) {
- fmt.Printf("TestGetConversationRecvMessageOpt , success,data:%v\n", data)
-}
-
-func DoTestCreateTextMessage(text string) string {
- operationID := utils.OperationIDGenerator()
- return open_im_sdk.CreateTextMessage(operationID, text)
-}
-
-func DoTestCreateImageMessageFromFullPath() string {
- operationID := utils.OperationIDGenerator()
- return open_im_sdk.CreateImageMessageFromFullPath(operationID, "C:\\Users\\Administrator\\Desktop\\rtc.proto")
- //open_im_sdk.SendMessage(&testSendMsg, operationID, s, , "", utils.StructToJsonString(o))
-}
-
-func DoTestCreateOtherMessageFromFullPath() string {
- operationID := utils.OperationIDGenerator()
- return open_im_sdk.CreateFileMessageFromFullPath(operationID, "C:\\Users\\Administrator\\Desktop\\2.txt", "2.txt")
- //open_im_sdk.SendMessage(&testSendMsg, operationID, s, , "", utils.StructToJsonString(o))
-}
-
-func DoTestCreateVideoMessageFromFullPath() string {
- operationID := utils.OperationIDGenerator()
- return open_im_sdk.CreateVideoMessageFromFullPath(operationID, "C:\\Users\\Administrator\\Desktop\\video_test.mp4", "mp4", 5, "C:\\Users\\Administrator\\Desktop\\shot.jpg")
-}
-
-// funcation DoTestSetConversationDraft() {
-// var test TestSetConversationDraft
-// open_im_sdk.SetConversationDraft("single_c93bc8b171cce7b9d1befb389abfe52f", "hah", test)
-//
-// }
-type TestSetConversationDraft struct {
-}
-
-func (t TestSetConversationDraft) OnError(errCode int32, errMsg string) {
- fmt.Printf("SetConversationDraft , OnError %v\n", errMsg)
-}
-
-func (t TestSetConversationDraft) OnSuccess(data string) {
- fmt.Printf("SetConversationDraft , OnSuccess %v\n", data)
-}
-
-type GetHistoryCallBack struct {
- OperationID string
- Data string
-}
-
-func (g GetHistoryCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "GetHistoryCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g GetHistoryCallBack) OnSuccess(data string) {
- g.Data = data
- log.ZInfo(ctx, "get History success. ", "operationID", g.OperationID, "data", g.Data)
-}
-
-type SetAppBadgeCallBack struct {
- OperationID string
-}
-
-func (g SetAppBadgeCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "SetAppBadgeCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g SetAppBadgeCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "SetAppBadgeCallBack success", "operationID", g.OperationID, "data", data)
-}
-
-type UpdateFcmTokenCallBack struct {
- OperationID string
-}
-
-func (g UpdateFcmTokenCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "UpdateFcmTokenCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g UpdateFcmTokenCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "UpdateFcmTokenCallBack success", "operationID", g.OperationID, "data", data)
-}
-
-type FindMessageListCallBack struct {
- OperationID string
-}
-
-func (g FindMessageListCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "FindMessageListCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g FindMessageListCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "FindMessageListCallBack success", "operationID", g.OperationID, "data", data)
-}
-
-type SetMessageReactionExtensionsCallBack struct {
- OperationID string
-}
-
-func (g SetMessageReactionExtensionsCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "SetMessageReactionExtensionsCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g SetMessageReactionExtensionsCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "SetMessageReactionExtensionsCallBack success", "operationID", g.OperationID, "data", data)
-}
-
-type AddMessageReactionExtensionsCallBack struct {
- OperationID string
-}
-
-func (g AddMessageReactionExtensionsCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "AddMessageReactionExtensionsCallBack err", "operationID", g.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (g AddMessageReactionExtensionsCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "AddMessageReactionExtensionsCallBack success", "operationID", g.OperationID, "data", data)
-}
-
-type GetMessageListReactionExtensionsCallBack struct {
- OperationID string
-}
-
-func (g GetMessageListReactionExtensionsCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, g.OperationID, "GetMessageListReactionExtensionsCallBack err", errCode, "errMsg", errMsg)
-}
-
-func (g GetMessageListReactionExtensionsCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, g.OperationID, "GetMessageListReactionExtensionsCallBack success. data", data)
-}
-
-type GetHistoryReverseCallBack struct {
- OperationID string
-}
-
-func (g GetHistoryReverseCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, g.OperationID, "GetHistoryReverseCallBack err", errCode, "errMsg", errMsg)
-}
-
-func (g GetHistoryReverseCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, g.OperationID, "GetHistoryReverseCallBack success. data", data)
-}
-
-type SearchLocalMessagesCallBack struct {
- OperationID string
-}
-
-func (g SearchLocalMessagesCallBack) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, g.OperationID, "SearchLocalMessagesCallBack err", errCode, "errMsg", errMsg)
-}
-
-func (g SearchLocalMessagesCallBack) OnSuccess(data string) {
- fmt.Println(g.OperationID, "SearchLocalMessagesCallBack success. data", data)
-}
-
-type MsgListenerCallBak struct {
-}
-
-func (m *MsgListenerCallBak) OnMsgDeleted(s string) {}
-
-func (m *MsgListenerCallBak) OnRecvOfflineNewMessage(message string) {
- //TODO implement me
- panic("implement me")
-}
-
-func (m *MsgListenerCallBak) OnRecvMessageExtensionsAdded(msgID string, reactionExtensionList string) {
- fmt.Printf("OnRecvMessageExtensionsAdded %v %v", msgID, reactionExtensionList)
- log.ZInfo(ctx, "internal OnRecvMessageExtensionsAdded", "msgID", msgID, "reactionExtensionList", reactionExtensionList)
-}
-
-func (m *MsgListenerCallBak) OnRecvGroupReadReceipt(groupMsgReceiptList string) {
- //fmt.Println("OnRecvC2CReadReceipt , ", groupMsgReceiptList)
-}
-
-func (m *MsgListenerCallBak) OnNewRecvMessageRevoked(messageRevoked string) {
- //fmt.Println("OnNewRecvMessageRevoked , ", messageRevoked)
-}
-
-func (m *MsgListenerCallBak) OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string) {
- log.ZInfo(ctx, "internal OnRecvMessageExtensionsChanged", "msgID", msgID, "reactionExtensionList", reactionExtensionList)
-}
-
-func (m *MsgListenerCallBak) OnRecvMessageExtensionsDeleted(msgID string, reactionExtensionKeyList string) {
- log.ZInfo(ctx, "internal OnRecvMessageExtensionsDeleted", "msgID", msgID, "reactionExtensionKeyList", reactionExtensionKeyList)
-}
-
-func (m *MsgListenerCallBak) OnRecvOnlineOnlyMessage(message string) {
-
-}
-
-type BatchMsg struct {
-}
-
-func (m *BatchMsg) OnRecvNewMessages(groupMsgReceiptList string) {
- log.ZInfo(ctx, "OnRecvNewMessages", "groupMsgReceiptList", groupMsgReceiptList)
-}
-
-func (m *BatchMsg) OnRecvOfflineNewMessages(messageList string) {
- log.ZInfo(ctx, "OnRecvOfflineNewMessages", "messageList", messageList)
-}
-
-func (m *MsgListenerCallBak) OnRecvNewMessage(msg string) {
- var mm sdk_struct.MsgStruct
- err := json.Unmarshal([]byte(msg), &mm)
- if err != nil {
- log.ZError(ctx, "Unmarshal failed", err, "msg", msg)
- } else {
- RecvMsgMapLock.Lock()
- defer RecvMsgMapLock.Unlock()
- t := SendRecvTime{SendIDRecvID: mm.SendID + mm.RecvID, RecvTime: utils.GetCurrentTimestampByMill()}
- RecvAllMsg[mm.ClientMsgID] = &t
- log.ZInfo(ctx, "", "OnRecvNewMessage callback", "ClientMsgID", mm.ClientMsgID, "SendID", mm.SendID, "RecvID", mm.RecvID)
- }
-}
-
-type TestSearchLocalMessages struct {
- OperationID string
-}
-
-func (t TestSearchLocalMessages) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "SearchLocalMessages , OnError", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t TestSearchLocalMessages) OnSuccess(data string) {
- log.ZInfo(ctx, "SearchLocalMessages , OnSuccess", "operationID", t.OperationID, "data", data)
-}
-
-//funcation DoTestSearchLocalMessages() {
-// var t TestSearchLocalMessages
-// operationID := utils.OperationIDGenerator()
-// t.OperationID = operationID
-// var p sdk_params_callback.SearchLocalMessagesParams
-// //p.SessionType = constant.SingleChatType
-// p.SourceID = "18090680773"
-// p.KeywordList = []string{}
-// p.SearchTimePeriod = 24 * 60 * 60 * 10
-// open_im_sdk.SearchLocalMessages(t, operationID, utils.StructToJsonString(p))
-//}
-
-type TestDeleteConversation struct {
- OperationID string
-}
-
-func (t TestDeleteConversation) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestDeleteConversation , OnError", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t TestDeleteConversation) OnSuccess(data string) {
- log.ZInfo(ctx, "TestDeleteConversation , OnSuccess", "operationID", t.OperationID, "data", data)
-}
-
-func (m MsgListenerCallBak) OnRecvC2CReadReceipt(data string) {
- fmt.Println("OnRecvC2CReadReceipt , ", data)
-}
-
-func (m MsgListenerCallBak) OnRecvMessageRevoked(msgId string) {
- fmt.Println("OnRecvMessageRevoked ", msgId)
-}
-
-type conversationCallBack struct {
- SyncFlag int
-}
-
-func (c *conversationCallBack) OnRecvMessageExtensionsChanged(msgID string, reactionExtensionList string) {
- panic("implement me")
-}
-
-func (c *conversationCallBack) OnRecvMessageExtensionsDeleted(msgID string, reactionExtensionKeyList string) {
- panic("implement me")
-}
-func (c *conversationCallBack) OnSyncServerProgress(progress int) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "progress", progress)
-}
-
-func (c *conversationCallBack) OnSyncServerStart(reinstalled bool) {
-
-}
-
-func (c *conversationCallBack) OnSyncServerFinish(reinstalled bool) {
- c.SyncFlag = 1
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (c *conversationCallBack) OnSyncServerFailed(reinstalled bool) {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (c *conversationCallBack) OnNewConversation(conversationList string) {
- log.ZInfo(ctx, "OnNewConversation.", "returnList is", conversationList)
-}
-
-func (c *conversationCallBack) OnConversationChanged(conversationList string) {
- log.ZInfo(ctx, "OnConversationChanged. ", "returnList is", conversationList)
-}
-
-func (c *conversationCallBack) OnTotalUnreadMessageCountChanged(totalUnreadCount int32) {
- log.ZInfo(ctx, "OnTotalUnreadMessageCountChanged. ", "returnTotalUnreadCount is", totalUnreadCount)
-}
-
-func (c *conversationCallBack) OnConversationUserInputStatusChanged(change string) {
-
-}
-
-type testMarkC2CMessageAsRead struct {
-}
-
-func (testMarkC2CMessageAsRead) OnSuccess(data string) {
- fmt.Println(" testMarkC2CMessageAsRead OnSuccess", data)
-}
-
-func (testMarkC2CMessageAsRead) OnError(code int32, msg string) {
- fmt.Println("testMarkC2CMessageAsRead, OnError", code, msg)
-}
-
-//funcation DoTestMarkC2CMessageAsRead() {
-// var test testMarkC2CMessageAsRead
-// readid := "2021-06-23 12:25:36-7eefe8fc74afd7c6adae6d0bc76929e90074d5bc-8522589345510912161"
-// var xlist []string
-// xlist = append(xlist, readid)
-// jsonid, _ := json.Marshal(xlist)
-// open_im_sdk.MarkC2CMessageAsRead(test, Friend_uid, string(jsonid))
-//}
-
-type SendRecvTime struct {
- SendTime int64
- SendSeccCallbackTime int64
- RecvTime int64
- SendIDRecvID string
-}
-
-var SendSuccAllMsg map[string]*SendRecvTime //msgid->send+recv:
-var SendFailedAllMsg map[string]string
-var RecvAllMsg map[string]*SendRecvTime //msgid->send+recv
-var SendMsgMapLock sync.RWMutex
-var RecvMsgMapLock sync.RWMutex
-
-func init() {
- SendSuccAllMsg = make(map[string]*SendRecvTime)
- SendFailedAllMsg = make(map[string]string)
- RecvAllMsg = make(map[string]*SendRecvTime)
-
-}
-
-func DoTestSetAppBackgroundStatus(isBackground bool) {
- var testSendMsg TestSendMsgCallBack
- operationID := utils.OperationIDGenerator()
- open_im_sdk.SetAppBackgroundStatus(&testSendMsg, operationID, isBackground)
-}
-func DoTestSendMsg2(sendId, recvID string) {
- m := "Single chat test" + sendId + ":" + recvID + ":"
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateTextMessage(m)
- log.ZInfo(ctx, "send msg:", "operationID", operationID, "message", s) // 修改此行
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "121313"
- o.Desc = "45464"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, recvID, "", utils.StructToJsonString(&o), false)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "success", "sendId", sendId, "recvID", recvID) // 修改此行
-}
-
-func DoTestSendMsg2Group(sendId, groupID string, index int) {
- m := "test: " + sendId + " : " + groupID + " : " + utils.IntToString(index)
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateTextMessage(m)
- log.ZInfo(ctx, "send msg:", "operationID", operationID, "message", s) // 修改此行
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "Title"
- o.Desc = "Desc"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, "", groupID, utils.StructToJsonString(&o), false)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "success") // 修改此行
-}
-
-func DoTestSendMsg2GroupWithMessage(sendId, groupID string, message string) {
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateTextMessage(message)
- log.ZInfo(ctx, "send msg:", "operationID", operationID, "message", s) // 修改此行
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "Title"
- o.Desc = "Desc"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, "", groupID, utils.StructToJsonString(&o), false)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "success") // 修改此行
-}
-
-func DoTestSendMsg2c2c(sendId, recvID string, index int) {
- m := "test: " + sendId + " : " + recvID + " : " + utils.IntToString(index)
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateTextMessage(m)
- log.ZInfo(ctx, "send msg:", "operationID", operationID, "message", s) // 修改此行
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "Title"
- o.Desc = "Desc"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, recvID, "", utils.StructToJsonString(&o), false)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "success") // 修改此行
-}
-
-type TestMarkGroupMessageAsRead struct {
- OperationID string
-}
-
-func (t TestMarkGroupMessageAsRead) OnError(errCode int32, errMsg string) {
-
- log.ZInfo(ctx, "TestMarkGroupMessageAsRead , OnError", "operationID", t.OperationID, "errMsg", errMsg) // 修改此行
-}
-
-func (t TestMarkGroupMessageAsRead) OnSuccess(data string) {
- log.ZInfo(ctx, "TestMarkGroupMessageAsRead , OnSuccess", "operationID", t.OperationID, "data", data) // 修改此行
-}
-
-func DoTestSendMsg(index int, sendId, recvID string, groupID string, idx string) {
- m := "test msg " + sendId + ":" + recvID + ":" + idx
- operationID := utils.OperationIDGenerator()
- ctx := mcontext.NewCtx(operationID)
- s, err := allLoginMgr[index].mgr.Conversation().CreateTextMessage(ctx, m)
- if err != nil {
- log.ZError(ctx, "CreateTextMessage", err, "operationID", operationID)
- return
- }
-
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- testSendMsg.sendTime = utils.GetCurrentTimestampByMill()
- o := sdkws.OfflinePushInfo{}
- o.Title = "title"
- o.Desc = "desc"
- testSendMsg.sendID = sendId
- testSendMsg.recvID = recvID
- testSendMsg.groupID = groupID
- testSendMsg.msgID = s.ClientMsgID
- log.ZInfo(ctx, "SendMessage", "operationID", testSendMsg.OperationID, "sendId", testSendMsg.sendID, "recvID", testSendMsg.recvID, "groupID", testSendMsg.groupID, "msgID",
- testSendMsg.msgID, "index", index)
- if recvID != "" {
- allLoginMgr[index].mgr.Conversation().SendMessage(ctx, s, recvID, "", &o, false)
- } else {
- allLoginMgr[index].mgr.Conversation().SendMessage(ctx, s, "", groupID, &o, false)
- }
- SendMsgMapLock.Lock()
- defer SendMsgMapLock.Unlock()
- x := SendRecvTime{SendTime: testSendMsg.sendTime}
- SendSuccAllMsg[testSendMsg.msgID] = &x
-}
-
-//
-//funcation DoTestSendMsgPress(index int, sendId, recvID string, idx string) {
-// m := "test msg " + sendId + ":" + recvID + ":" + idx
-// operationID := utils.OperationIDGenerator()
-// s := DoTestCreateTextMessageReliability(allLoginMgr[index].mgr, m)
-// var mstruct sdk_struct.MsgStruct
-// _ = json.Unmarshal([]byte(s), &mstruct)
-//
-// var testSendMsg TestSendMsgCallBackPress
-// testSendMsg.OperationID = operationID
-// o := server_api_params.OfflinePushInfo{}
-// o.Title = "title"
-// o.Desc = "desc"
-// testSendMsg.sendID = sendId
-// testSendMsg.recvID = recvID
-// testSendMsg.msgID = mstruct.ClientMsgID
-//
-// log.Warn(operationID, "SendMessage", sendId, recvID, testSendMsg.msgID, index)
-//
-// allLoginMgr[index].mgr.Conversation().SendMessage(&testSendMsg, s, recvID, "", utils.StructToJsonString(o), operationID)
-//}
-
-func DoTestSendImageMsg(recvID string) {
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateImageMessageFromFullPath()
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "121313"
- o.Desc = "45464"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, recvID, "", utils.StructToJsonString(&o), false)
-}
-
-//funcation DotestUploadFile() {
-// operationID := utils.OperationIDGenerator()
-// var testSendMsg TestSendMsgCallBack
-// open_im_sdk.UploadFile(&testSendMsg, operationID, "C:\\Users\\Administrator\\Desktop\\video_test.mp4")
-//}
-
-func DoTestSendOtherMsg(sendId, recvID string) {
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateOtherMessageFromFullPath()
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "121313"
- o.Desc = "45464"
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, recvID, "", utils.StructToJsonString(&o), false)
-}
-
-func DoTestSendVideo(sendId, recvID string) {
- operationID := utils.OperationIDGenerator()
- s := DoTestCreateVideoMessageFromFullPath()
- var testSendMsg TestSendMsgCallBack
- testSendMsg.OperationID = operationID
- o := sdkws.OfflinePushInfo{}
- o.Title = "121313"
- o.Desc = "45464"
- log.ZInfo(ctx, "SendMessage", "operationID", operationID, "message", s)
- open_im_sdk.SendMessage(&testSendMsg, operationID, s, recvID, "", utils.StructToJsonString(&o), false)
-}
-
-type TestClearMsg struct {
- OperationID string
-}
-
-func (t *TestClearMsg) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestClearMsg OnError", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t *TestClearMsg) OnSuccess(data string) {
- log.ZInfo(ctx, "TestClearMsg OnSuccess", "operationID", t.OperationID, "data", data)
-}
-
-func DoTestClearMsg() {
- var test TestClearMsg
- operationID := utils.OperationIDGenerator()
- open_im_sdk.DeleteAllMsgFromLocalAndSvr(&test, operationID)
-
-}
-
-type TestModifyGroupMessageReaction struct {
- OperationID string
-}
-
-func (t *TestModifyGroupMessageReaction) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "TestModifyGroupMessageReaction OnError", "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func (t *TestModifyGroupMessageReaction) OnSuccess(data string) {
- log.ZInfo(ctx, "TestModifyGroupMessageReaction OnSuccess", "operationID", t.OperationID, "data", data)
-}
-
-func DoTestGetSelfUserInfo() {
- var test TestModifyGroupMessageReaction
- open_im_sdk.GetSelfUserInfo(&test, "s")
-}
diff --git a/test/t_friend_sdk.go b/test/t_friend_sdk.go
deleted file mode 100644
index 6a3859579..000000000
--- a/test/t_friend_sdk.go
+++ /dev/null
@@ -1,792 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/tools/log"
- X "log"
- "os"
- "runtime"
- "time"
-
- "github.com/openimsdk/protocol/sdkws"
-
- "github.com/openimsdk/tools/mcontext"
-)
-
-var loggerf *X.Logger
-
-func init() {
- loggerf = X.New(os.Stdout, "", X.Llongfile|X.Ltime|X.Ldate)
-}
-
-type TestSendImg struct {
-}
-
-func (TestSendImg) OnSuccess(data string) {
- fmt.Println("testSendImg, OnSuccess, output: ", data)
-}
-
-func (TestSendImg) OnError(code int, msg string) {
- fmt.Println("testSendImg, OnError, ", code, msg)
-}
-
-func (TestSendImg) OnProgress(progress int) {
- fmt.Println("progress: ", progress)
-}
-
-func TestLog(v ...interface{}) {
- //X.SetFlags(X.Lshortfile | X.LstdFlags)
- loggerf.Println(v)
- a, b, c, d := runtime.Caller(1)
- X.Println(a, b, c, d)
-}
-
-var Friend_uid = "3126758667"
-
-func SetTestFriendID(friendUserID string) {
- Friend_uid = friendUserID
-}
-
-///////////////////////////////////////////////////////////
-
-type testGetFriendApplicationList struct {
- baseCallback
-}
-
-func DoTestGetFriendApplicationList() {
- var test testGetFriendApplicationList
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, test.OperationID, utils.GetSelfFuncName(), "input ")
- // open_im_sdk.GetRecvFriendApplicationList(test, test.OperationID)
-}
-
-// ////////////////////////////////////////////////////////`
-type testSetSelfInfo struct {
- baseCallback
-}
-
-func DoTestSetSelfInfo() {
- var test testSetSelfInfo
- test.OperationID = utils.OperationIDGenerator()
- userInfo := sdkws.UserInfo{}
- userInfo.Nickname = "new 4444444444444 Gordon001"
- jsonString := utils.StructToJsonStringDefault(userInfo)
- fmt.Println("SetSelfInfo, input: ")
- open_im_sdk.SetSelfInfo(test, test.OperationID, jsonString)
-}
-
-// ///////////////////////////////////////////////////////
-type testGetUsersInfo struct {
- baseCallback
-}
-
-func DoTestGetUsersInfo() {
- var test testGetUsersInfo
- test.OperationID = utils.OperationIDGenerator()
- userIDList := []string{"4950399653"}
- list := utils.StructToJsonStringDefault(userIDList)
- fmt.Println("testGetUsersInfo, input: ", list)
- open_im_sdk.GetUsersInfo(test, test.OperationID, list)
-}
-
-// ///////////////////////////////////////////////////////
-type testGetFriendsInfo struct {
- uid []string //`json:"uidList"`
-}
-
-func (testGetFriendsInfo) OnSuccess(data string) {
- fmt.Println("DoTestGetDesignatedFriendsInfo, OnSuccess, output: ", data)
-}
-
-func (testGetFriendsInfo) OnError(code int32, msg string) {
- fmt.Println("DoTestGetDesignatedFriendsInfo, OnError, ", code, msg)
-}
-
-func DoTestGetDesignatedFriendsInfo() {
- var test testGetFriendsInfo
- test.uid = append(test.uid, Friend_uid)
-
- jsontest, _ := json.Marshal(test.uid)
- fmt.Println("testGetFriendsInfo, input: ", string(jsontest))
- // open_im_sdk.GetDesignatedFriendsInfo(test, "xxxxxxxxxxx", string(jsontest))
-}
-
-// /////////////////////////////////////////////////////
-type testAddToBlackList struct {
- OperationID string
-}
-
-func (t testAddToBlackList) OnSuccess(string) {
- log.ZInfo(ctx, t.OperationID, "testAddToBlackList, OnSuccess")
-}
-
-func (t testAddToBlackList) OnError(code int32, msg string) {
- log.ZInfo(ctx, t.OperationID, "testAddToBlackList, OnError, ", code, msg)
-}
-
-func DoTestAddToBlackList(userID string) {
- var test testAddToBlackList
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.AddBlack(test, test.OperationID, userID, "")
-}
-
-// /////////////////////////////////////////////////////
-type testDeleteFromBlackList struct {
- baseCallback
-}
-
-func DoTestDeleteFromBlackList(userID string) {
- var test testDeleteFromBlackList
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.RemoveBlack(test, test.OperationID, userID)
-}
-
-// ////////////////////////////////////////////////////
-type testGetBlackList struct {
- OperationID string
-}
-
-func (t testGetBlackList) OnSuccess(data string) {
- log.ZInfo(ctx, t.OperationID, "testGetBlackList, OnSuccess, output: ", data)
-}
-func (t testGetBlackList) OnError(code int32, msg string) {
- log.ZInfo(ctx, t.OperationID, "testGetBlackList, OnError, ", code, msg)
-}
-func DoTestGetBlackList() {
- var test testGetBlackList
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetBlackList(test, test.OperationID)
-}
-
-//////////////////////////////////////////////////////
-
-type testCheckFriend struct {
- OperationID string
-}
-
-func (t testCheckFriend) OnSuccess(data string) {
- log.ZInfo(ctx, t.OperationID, "testCheckFriend, OnSuccess, output: ", data)
-}
-func (t testCheckFriend) OnError(code int32, msg string) {
- log.ZInfo(ctx, t.OperationID, "testCheckFriend, OnError, ", code, msg)
-}
-func DoTestCheckFriend() {
- var test testCheckFriend
- test.OperationID = utils.OperationIDGenerator()
- userIDList := []string{"openIM100"}
- list := utils.StructToJsonString(userIDList)
- fmt.Println("CheckFriend, input: ", list)
- open_im_sdk.CheckFriend(test, test.OperationID, list)
-}
-
-// /////////////////////////////////////////////////////////
-type testSetFriendRemark struct {
- baseCallback
-}
-
-func DotestSetFriendRemark() {
- var test testSetFriendRemark
- test.OperationID = utils.OperationIDGenerator()
-
- var param sdk_params_callback.SetFriendRemarkParams
- param.ToUserID = Friend_uid
- param.Remark = "4444 "
- jsontest := utils.StructToJsonString(param)
- log.ZInfo(ctx, test.OperationID, utils.GetSelfFuncName(), "input ", jsontest)
- open_im_sdk.SetFriendRemark(test, test.OperationID, jsontest)
-}
-
-/////////////////////
-////////////////////////////////////////////////////////
-
-type testDeleteFriend struct {
- baseCallback
-}
-
-func DotestDeleteFriend(userID string) {
- var test testDeleteFriend
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.DeleteFriend(test, test.OperationID, userID)
-}
-
-// /////////////////////////////////////////////////////
-// ///////////////////////////////////////////////////////
-type testaddFriend struct {
- OperationID string
-}
-
-func (t testaddFriend) OnSuccess(data string) {
- log.ZInfo(ctx, t.OperationID, "testaddFriend, OnSuccess", data)
-}
-func (t testaddFriend) OnError(code int32, msg string) {
- log.ZInfo(ctx, t.OperationID, "testaddFriend, OnError", code, msg)
-}
-
-func DoTestAddFriend() {
- var test testaddFriend
- test.OperationID = utils.OperationIDGenerator()
- params := sdk_params_callback.AddFriendParams{
- ToUserID: Friend_uid,
- ReqMsg: "777777777777777777777777",
- }
- jsontestaddFriend := utils.StructToJsonString(params)
- log.ZInfo(ctx, test.OperationID, "addFriend input:", jsontestaddFriend)
- open_im_sdk.AddFriend(test, test.OperationID, jsontestaddFriend)
-}
-
-////////////////////////////////////////////////////////////////////
-
-// ///////////////////////////////////////////////////////
-type testGetSendFriendApplicationList struct {
- OperationID string
-}
-
-func (t testGetSendFriendApplicationList) OnSuccess(data string) {
- log.ZInfo(ctx, t.OperationID, "testGetSendFriendApplicationList, OnSuccess", data)
-}
-func (t testGetSendFriendApplicationList) OnError(code int32, msg string) {
- log.ZInfo(ctx, t.OperationID, "testGetSendFriendApplicationList, OnError", code, msg)
-}
-
-func DoTestGetSendFriendApplicationList() {
- var test testGetSendFriendApplicationList
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, test.OperationID, "GetSendFriendApplicationList input:")
- // open_im_sdk.GetSendFriendApplicationList(test, test.OperationID)
-}
-
-////////////////////////////////////////////////////////////////////
-
-type testGetFriendList struct {
- baseCallback
-}
-
-func DotestGetFriendList() {
- var test testGetFriendList
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, test.OperationID, utils.GetSelfFuncName(), "input ")
- open_im_sdk.GetFriendList(test, test.OperationID)
-}
-
-type testSearchFriends struct {
- baseCallback
-}
-
-func DotestSearchFriends() {
- var test testSearchFriends
- test.OperationID = utils.OperationIDGenerator()
- test.callName = "SearchFriends"
- var params sdk_params_callback.SearchFriendsParam
- params.KeywordList = []string{"G"}
- params.IsSearchUserID = true
- params.IsSearchNickname = true
- params.IsSearchRemark = true
- log.ZInfo(ctx, test.OperationID, utils.GetSelfFuncName(), "input ", params)
- open_im_sdk.SearchFriends(test, test.OperationID, utils.StructToJsonString(params))
-}
-
-/////////////////////////////////////////////////////////////////////
-
-type testAcceptFriendApplication struct {
- baseCallback
-}
-
-func DoTestAcceptFriendApplication() {
- var test testAcceptFriendApplication
- test.OperationID = utils.OperationIDGenerator()
- var param sdk_params_callback.ProcessFriendApplicationParams
- param.HandleMsg = "ok ok "
- param.ToUserID = Friend_uid
- input := utils.StructToJsonString(param)
- open_im_sdk.AcceptFriendApplication(test, test.OperationID, input)
-}
-
-type testRefuseFriendApplication struct {
- baseCallback
-}
-
-func DoTestRefuseFriendApplication() {
- var test testRefuseFriendApplication
- test.OperationID = utils.OperationIDGenerator()
- var param sdk_params_callback.ProcessFriendApplicationParams
- param.HandleMsg = "nonono"
- param.ToUserID = Friend_uid
- input := utils.StructToJsonString(param)
- open_im_sdk.RefuseFriendApplication(test, test.OperationID, input)
-}
-
-/*
-type testRefuseFriendApplication struct {
- ui2AcceptFriend
-}
-
-func (testRefuseFriendApplication) OnSuccess(info string) {
- fmt.Println("RefuseFriendApplication OnSuccess", info)
-}
-func (testRefuseFriendApplication) OnError(code int, msg string) {
- fmt.Println("RefuseFriendApplication, OnError, ", code, msg)
-}
-*/
-/*
-func DoTestRefuseFriendApplication() {
-
- var test testRefuseFriendApplication
- test.UID = Friend_uid
-
- js, _ := json.Marshal(test)
- RefuseFriendApplication(test, string(js))
- fmt.Println("RefuseFriendApplication, input: ", string(js))
-}
-
-
-*/
-
-/////////////////////////////////////////////////////////////////////
-
-//type testRefuseFriendApplication struct {
-// open_im_sdk.ui2AcceptFriend
-//}
-//
-//func (testRefuseFriendApplication) OnSuccess(info string) {
-// fmt.Println("testRefuseFriendApplication OnSuccess", info)
-//}
-//func (testRefuseFriendApplication) OnError(code int32, msg string) {
-// fmt.Println("testRefuseFriendApplication, OnError, ", code, msg)
-//}
-//func DoTestRefuseFriendApplication() {
-// var testRefuseFriendApplication testRefuseFriendApplication
-// testRefuseFriendApplication.ui2AcceptFriend = Friend_uid
-//
-// jsontestfusetFriendappclicatrion, _ := json.Marshal(testRefuseFriendApplication.UID)
-// open_im_sdk.RefuseFriendApplication(testRefuseFriendApplication, string(jsontestfusetFriendappclicatrion), "")
-// fmt.Println("RefuseFriendApplication, input: ", string(jsontestfusetFriendappclicatrion))
-//}
-
-////////////////////////////////////////////////////////////////////
-
-func SetListenerAndLogin(uid, tk string) {
- //
- //var testConversation conversationCallBack
- //open_im_sdk.SetConversationListener(&testConversation)
- //
- //var testUser userCallback
- //open_im_sdk.SetUserListener(testUser)
- //
- //var msgCallBack MsgListenerCallBak
- //open_im_sdk.SetAdvancedMsgListener(&msgCallBack)
- //
- //var batchMsg BatchMsg
- //open_im_sdk.SetBatchMsgListener(&batchMsg)
- //
- //var friendListener testFriendListener
- //open_im_sdk.SetFriendListener(friendListener)
- //
- //var groupListener testGroupListener
- //open_im_sdk.SetGroupListener(groupListener)
- //var signalingListener testSignalingListener
- //open_im_sdk.SetSignalingListener(&signalingListener)
- //
- //var organizationListener testOrganizationListener
- //open_im_sdk.SetOrganizationListener(organizationListener)
- //
- //var workMomentsListener testWorkMomentsListener
- //open_im_sdk.SetWorkMomentsListener(workMomentsListener)
-
- //InOutlllogin(uid, tk)
-
- log.ZWarn(ctx, "SetListenerAndLogin fin", errors.New(""))
-}
-
-func lllogin(uid, tk string) bool {
- var callback BaseSuccessFailed
- callback.funcName = utils.GetSelfFuncName()
- operationID := utils.OperationIDGenerator()
- open_im_sdk.Login(&callback, uid, operationID, tk)
-
- for true {
- if callback.errCode == 1 {
- fmt.Println("success code 1")
- return true
- } else if callback.errCode == -1 {
- fmt.Println("failed code -1")
- return false
- } else {
- fmt.Println("code sleep")
- time.Sleep(1 * time.Second)
- continue
- }
- }
- return true
-}
-func ReliabilityInitAndLogin(index int, uid, tk, ws, api string) {
- var cf sdk_struct.IMConfig
- cf.ApiAddr = api
- cf.WsAddr = ws
- cf.PlatformID = 1
- cf.DataDir = "./"
- cf.IsLogStandardOutput = true
- cf.LogLevel = uint32(LogLevel)
-
- operationID := utils.OperationIDGenerator()
-
- ctx := mcontext.NewCtx(operationID)
- var testinit testInitLister
- lg := new(open_im_sdk.LoginMgr)
- log.ZInfo(ctx, "DoReliabilityTest", "UID", uid, "Token", tk, "WS", ws, "API", api)
- log.ZInfo(ctx, "New login manager ", "OperationID", operationID)
-
- allLoginMgr[index].mgr = lg
- lg.InitSDK(cf, &testinit)
-
- ctx = ccontext.WithOperationID(lg.Context(), operationID)
-
- log.ZInfo(ctx, "Initialized SDK with config", "Config", cf)
-
- var testConversation conversationCallBack
- lg.SetConversationListener(&testConversation)
-
- var testUser userCallback
- lg.SetUserListener(testUser)
-
- var msgCallBack MsgListenerCallBak
- lg.SetAdvancedMsgListener(&msgCallBack)
-
- var friendListener testFriendListener
- lg.SetFriendListener(friendListener)
-
- var groupListener testGroupListener
- lg.SetGroupListener(groupListener)
-
- var callback BaseSuccessFailed
- callback.funcName = utils.GetSelfFuncName()
-
- for {
- if callback.errCode == 1 && testConversation.SyncFlag == 1 {
- lg.User().GetSelfUserInfo(ctx)
- return
- }
- }
-}
-
-func PressInitAndLogin(index int, uid, tk, ws, api string) {
- var cf sdk_struct.IMConfig
- cf.ApiAddr = api
- cf.WsAddr = ws
- cf.PlatformID = 1
- cf.DataDir = "./"
- cf.LogLevel = uint32(LogLevel)
-
- operationID := utils.OperationIDGenerator()
- ctx := mcontext.NewCtx(operationID)
- var testinit testInitLister
- lg := new(open_im_sdk.LoginMgr)
- log.ZInfo(ctx, "DoReliabilityTest", "UID", uid, "Token", tk, "WS", ws, "API", api)
-
- log.ZInfo(ctx, "New login manager ", "OperationID", operationID)
-
- allLoginMgr[index].mgr = lg
- lg.InitSDK(cf, &testinit)
-
- log.ZInfo(ctx, "Initialized SDK with config", "Config", cf)
-
- var testConversation conversationCallBack
- lg.SetConversationListener(&testConversation)
-
- var testUser userCallback
- lg.SetUserListener(testUser)
-
- var msgCallBack MsgListenerCallBak
- lg.SetAdvancedMsgListener(&msgCallBack)
-
- var friendListener testFriendListener
- lg.SetFriendListener(friendListener)
-
- var groupListener testGroupListener
- lg.SetGroupListener(groupListener)
-
- err := lg.Login(ctx, uid, tk)
- if err != nil {
- log.ZError(ctx, "Login failed", err, "OperationID", operationID)
- }
-}
-
-func DoTest(uid, tk, ws, api string) {
- var cf sdk_struct.IMConfig
- cf.ApiAddr = api // "http://120.24.45.199:10000"
- cf.WsAddr = ws //"ws://120.24.45.199:17778"
- cf.PlatformID = 1
- cf.DataDir = "./"
-
- var s string
- b, _ := json.Marshal(cf)
- s = string(b)
- fmt.Println(s)
- var testinit testInitLister
- operationID := utils.OperationIDGenerator()
- if !open_im_sdk.InitSDK(&testinit, operationID, s) {
- log.ZError(ctx, "InitSDK failed", errors.New("InitSDK failed"))
- return
- }
-
- var testConversation conversationCallBack
- open_im_sdk.SetConversationListener(&testConversation)
-
- var testUser userCallback
- open_im_sdk.SetUserListener(testUser)
-
- //var msgCallBack MsgListenerCallBak
- //open_im_sdk.AddAdvancedMsgListener(msgCallBack)
-
- var friendListener testFriendListener
- open_im_sdk.SetFriendListener(friendListener)
-
- var groupListener testGroupListener
- open_im_sdk.SetGroupListener(groupListener)
-
- time.Sleep(1 * time.Second)
-
- for !lllogin(uid, tk) {
- fmt.Println("lllogin, failed, login...")
- time.Sleep(1 * time.Second)
- }
-
-}
-
-////////////////////////////////////////////////////////////////////
-
-type TestSendMsgCallBack struct {
- msg string
- OperationID string
- sendID string
- recvID string
- msgID string
- sendTime int64
- recvTime int64
- groupID string
-}
-
-func (t *TestSendMsgCallBack) OnError(errCode int32, errMsg string) {
- log.ZError(ctx, "test_openim: send msg failed: ", errors.New("test_openim: send msg failed: "),
- "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg, "msgID", t.msgID, "msg", t.msg)
- SendMsgMapLock.Lock()
- defer SendMsgMapLock.Unlock()
- SendFailedAllMsg[t.msgID] = t.sendID + t.recvID
-
-}
-
-func (t *TestSendMsgCallBack) OnSuccess(data string) {
- log.ZInfo(ctx, "test_openim: send msg success: |", "operationID", t.OperationID,
- "msgID", t.msgID, "msg", t.msg, "data", data)
- SendMsgMapLock.Lock()
- defer SendMsgMapLock.Unlock()
- //k, _ := SendSuccAllMsg[t.msgID]
- //k.SendSeccCallbackTime = utils.GetCurrentTimestampByMill()
- //k.SendIDRecvID = t.sendID + t.recvID
-}
-
-func (t *TestSendMsgCallBack) OnProgress(progress int) {
- // fmt.Printf("msg_send , onProgress %d\n", progress)
-}
-
-type TestSendMsgCallBackPress struct {
- msg string
- OperationID string
- sendID string
- recvID string
- msgID string
-}
-
-func (t *TestSendMsgCallBackPress) OnError(errCode int32, errMsg string) {
- log.ZWarn(ctx, "TestSendMsgCallBackPress: send msg failed: |", errors.New(""), "operationID", t.OperationID, "errCode",
- errCode, "errMsg", errMsg, "msgID", t.msgID, "msg", t.msg)
-}
-
-func (t *TestSendMsgCallBackPress) OnSuccess(data string) {
- log.ZInfo(ctx, "TestSendMsgCallBackPress: send msg success: |", "operationID", t.OperationID, "msgID", t.msgID, "msg", t.msg)
-}
-
-func (t *TestSendMsgCallBackPress) OnProgress(progress int) {
- // fmt.Printf("msg_send , onProgress %d\n", progress)
-}
-
-type BaseSuccessFailedTest struct {
- successData string
- errCode int
- errMsg string
- funcName string
-}
-
-func (b *BaseSuccessFailedTest) OnError(errCode int32, errMsg string) {
- b.errCode = -1
- b.errMsg = errMsg
- fmt.Println("22onError ", b.funcName, errCode, errMsg)
-}
-
-func (b *BaseSuccessFailedTest) OnSuccess(data string) {
- b.errCode = 1
- b.successData = data
- fmt.Println("22OnSuccess: ", b.funcName, data)
-}
-
-func InOutDoTestSendMsg(sendId, receiverID string) {
- m := "test:" + sendId + ":" + receiverID + ":"
- //s := CreateTextMessage(m)
- var testSendMsg TestSendMsgCallBack
- // testSendMsg.msg = SendMessage(&testSendMsg, s, receiverID, "", false)
- fmt.Println("func send ", m, testSendMsg.msg)
- fmt.Println("test to recv : ", receiverID)
-}
-
-//func DoTestGetAllConversationList() {
-// var test TestGetAllConversationListCallBack
-// open_im_sdk.GetAllConversationList(test)
-//}
-
-type userCallback struct {
-}
-
-func (c userCallback) OnUserStatusChanged(statusMap string) {
- log.ZInfo(ctx, "User Status Changed", "statusMap", statusMap)
-}
-
-func (userCallback) OnSelfInfoUpdated(callbackData string) {
- log.ZInfo(ctx, "Self Info Updated", "callbackData", callbackData)
-}
-func (userCallback) OnUserCommandAdd(callbackData string) {
- log.ZInfo(ctx, "User Command Added", "callbackData", callbackData)
-}
-func (userCallback) OnUserCommandUpdate(callbackData string) {
- log.ZInfo(ctx, "User Command Updated", "callbackData", callbackData)
-}
-func (userCallback) OnUserCommandDelete(callbackData string) {
- log.ZInfo(ctx, "User Command Deleted", "callbackData", callbackData)
-}
-
-// //////////////////////////////////////////////////////////////////
-type testInitLister struct {
-}
-
-func (t *testInitLister) OnUserTokenInvalid(errMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "errMsg", errMsg)
-}
-
-func (t *testInitLister) OnUserTokenExpired() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-func (t *testInitLister) OnConnecting() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnConnectSuccess() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnConnectFailed(ErrCode int32, ErrMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "errCode", ErrCode, "errMsg", ErrMsg)
-}
-
-func (t *testInitLister) OnKickedOffline() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnSelfInfoUpdated(info string) {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnUserCommandAdd(info string) {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-func (t *testInitLister) OnUserCommandDelete(info string) {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-func (t *testInitLister) OnUserCommandUpdates(info string) {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnSuccess() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (t *testInitLister) OnError(code int32, msg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "code", code, "msg", msg)
-}
-
-type testLogin struct {
-}
-
-func (testLogin) OnSuccess(string) {
- fmt.Println("testLogin OnSuccess")
-}
-
-func (testLogin) OnError(code int32, msg string) {
- fmt.Println("testLogin, OnError", code, msg)
-}
-
-type testFriendListener struct {
- x int
-}
-
-func (testFriendListener) OnFriendApplicationAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendApplicationDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendApplicationAccepted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendApplicationRejected(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnBlackAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnBlackDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnFriendInfoChanged(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo)
-}
-
-func (testFriendListener) OnSuccess() {
- log.ZInfo(ctx, utils.GetSelfFuncName())
-}
-
-func (testFriendListener) OnError(code int32, msg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "Code", code, "Message", msg)
-}
diff --git a/test/t_group_sdk.go b/test/t_group_sdk.go
deleted file mode 100644
index e0d040661..000000000
--- a/test/t_group_sdk.go
+++ /dev/null
@@ -1,533 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
-
- // "encoding/json"
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/tools/log"
- //"open_im_sdk/internal/open_im_sdk"
- //"open_im_sdk/pkg/utils"
- // "open_im_sdk/internal/common"
-)
-
-type XBase struct {
-}
-
-func (XBase) OnError(errCode int32, errMsg string) {
- fmt.Println("get groupmenberinfo OnError", errCode, errMsg)
-}
-func (XBase) OnSuccess(data string) {
- fmt.Println("get groupmenberinfo OnSuccess, ", data)
-}
-
-func (XBase) OnProgress(progress int) {
- fmt.Println("OnProgress, ", progress)
-}
-
-type testGroupListener struct {
-}
-
-func (testGroupListener) OnJoinedGroupAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnJoinedGroupDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupMemberAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupMemberDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupApplicationAdded(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupApplicationDeleted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupInfoChanged(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupMemberInfoChanged(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupApplicationAccepted(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupApplicationRejected(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-func (testGroupListener) OnGroupDismissed(callbackInfo string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "CallbackInfo", callbackInfo, "operationID", utils.OperationIDGenerator())
-}
-
-type testOrganizationListener struct {
-}
-
-func (testOrganizationListener) OnOrganizationUpdated() {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "on listener callback", "operationID", utils.OperationIDGenerator())
-}
-
-type testWorkMomentsListener struct {
-}
-
-func (testWorkMomentsListener) OnRecvNewNotification() {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "on listener callback", "operationID", utils.OperationIDGenerator())
-}
-
-type testCreateGroup struct {
- OperationID string
-}
-
-func (t testCreateGroup) OnSuccess(data string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "Data", data)
-}
-
-func (t testCreateGroup) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "ErrorCode", errCode, "ErrorMsg", errMsg)
-}
-
-func SetTestGroupID(groupID, memberID string) {
- MemberUserID = memberID
- TestgroupID = groupID
-}
-
-var MemberUserID = "2101502031"
-var me = "3984071717"
-var TestgroupID = "3109164461"
-
-func DoTestCreateGroup() {
- var test testCreateGroup
- test.OperationID = utils.OperationIDGenerator()
-
- var groupInfo sdk_params_callback.CreateGroupBaseInfoParam
- groupInfo.GroupName = "聊聊大群测试"
- groupInfo.GroupType = 1
-
- var memberlist []server_api_params.GroupAddMemberInfo
- memberlist = append(memberlist, server_api_params.GroupAddMemberInfo{UserID: MemberUserID, RoleLevel: 1})
- memberlist = append(memberlist, server_api_params.GroupAddMemberInfo{UserID: me, RoleLevel: 2})
-
- g1 := utils.StructToJsonString(groupInfo)
- g2 := utils.StructToJsonString(memberlist)
-
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID: ", test.OperationID, "g1", g1, "g2", g2)
- // open_im_sdk.CreateGroup(test, test.OperationID, g1, g2)
-}
-
-type testSetGroupInfo struct {
- OperationID string
-}
-
-func (t testSetGroupInfo) OnSuccess(data string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "data", data)
-}
-
-func (t testSetGroupInfo) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func DoSetGroupInfo() {
- var test testSetGroupInfo
- operationID := utils.OperationIDGenerator()
- test.OperationID = operationID
- var input sdk_params_callback.SetGroupInfoParam
- input.GroupName = "new group name 11111111"
- input.Notification = "new notification 11111"
- var n int32
- n = 1
- input.NeedVerification = &n
- setInfo := utils.StructToJsonString(input)
- // open_im_sdk.SetGroupInfo(test, operationID, TestgroupID, setInfo)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", operationID, "input: ", setInfo)
-}
-
-func DoSetGroupVerification() {
- var test testSetGroupInfo
- operationID := utils.OperationIDGenerator()
- test.OperationID = operationID
- open_im_sdk.SetGroupVerification(test, operationID, TestgroupID, 1)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", operationID, "input: ", TestgroupID, 2)
-}
-
-func DoSetGroupLookMemberInfo() {
- var test testSetGroupInfo
- operationID := utils.OperationIDGenerator()
- test.OperationID = operationID
- open_im_sdk.SetGroupLookMemberInfo(test, operationID, TestgroupID, 0)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", operationID, "input: ", TestgroupID, 1)
-}
-
-func DoSetGroupApplyMemberFriend() {
- var test testSetGroupInfo
- operationID := utils.OperationIDGenerator()
- test.OperationID = operationID
- open_im_sdk.SetGroupApplyMemberFriend(test, operationID, TestgroupID, 1)
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", operationID, "input: ", TestgroupID, 1)
-}
-
-type testGetGroupsInfo struct {
- OperationID string
-}
-
-func (t testGetGroupsInfo) OnSuccess(data string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "testGetGroupsInfo,onSuccess", data)
-}
-
-func (t testGetGroupsInfo) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "testGetGroupsInfo,onError", errCode, errMsg)
-}
-
-type testSearchGroups struct {
- OperationID string
-}
-
-func (t testSearchGroups) OnSuccess(data string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "data", data)
-}
-
-func (t testSearchGroups) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, utils.GetSelfFuncName(), "operationID", t.OperationID, "errCode", errCode, "errMsg", errMsg)
-}
-
-func DoTestGetGroupsInfo() {
- var test testGetGroupsInfo
- groupIDList := []string{TestgroupID}
- list := utils.StructToJsonString(groupIDList)
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DoTestGetGroupsInfo", "operationID", test.OperationID, "input", list)
- // open_im_sdk.GetGroupsInfo(test, test.OperationID, list)
-}
-
-func DoTestSearchGroups() {
- var test testGetGroupsInfo
- var params sdk_params_callback.SearchGroupsParam
- params.KeywordList = []string{"17"}
- //params.IsSearchGroupID =true
- params.IsSearchGroupName = true
- open_im_sdk.SearchGroups(test, test.OperationID, utils.StructToJsonString(params))
-}
-
-type testJoinGroup struct {
- OperationID string
-}
-
-func (t testJoinGroup) OnSuccess(data string) {
- log.ZInfo(ctx, "testJoinGroup", "operationID", t.OperationID, "onSuccess", data)
-}
-
-func (t testJoinGroup) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "testJoinGroup", "operationID", t.OperationID, "onError", errCode, errMsg)
-}
-
-func DoTestJoinGroup() {
- var test testJoinGroup
- test.OperationID = utils.OperationIDGenerator()
- groupID := "1003105543"
- reqMsg := "121212"
- ex := "ex"
- log.ZInfo(ctx, "testJoinGroup", "operationID", test.OperationID, "input", groupID, reqMsg, ex)
- open_im_sdk.JoinGroup(test, test.OperationID, groupID, reqMsg, constant.JoinBySearch, ex)
-}
-
-type testQuitGroup struct {
- OperationID string
-}
-
-func (t testQuitGroup) OnSuccess(data string) {
- log.ZInfo(ctx, "testQuitGroup", "operationID", t.OperationID, "onSuccess", data)
-}
-
-func (t testQuitGroup) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "testQuitGroup", "operationID", t.OperationID, "onError", errCode, errMsg)
-}
-func DoTestQuitGroup() {
- var test testQuitGroup
- test.OperationID = utils.OperationIDGenerator()
- groupID := "19de93b442a1ca3b772aa0f12761939d"
- log.ZInfo(ctx, "testQuitGroup", "operationID", test.OperationID, "input", groupID)
- open_im_sdk.QuitGroup(test, test.OperationID, groupID)
-}
-
-type testGetJoinedGroupList struct {
- OperationID string
-}
-
-/*
-OnError(errCode int, errMsg string)
-OnSuccess(data string)
-*/
-func (t testGetJoinedGroupList) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "testGetJoinedGroupList", "operationID", t.OperationID, "OnError", errCode, errMsg)
-}
-
-func (t testGetJoinedGroupList) OnSuccess(data string) {
- log.ZInfo(ctx, "testGetJoinedGroupList", "operationID", t.OperationID, "OnSuccess", "output", data)
-}
-
-func DoTestGetJoinedGroupList() {
- var test testGetJoinedGroupList
- test.OperationID = utils.OperationIDGenerator()
- open_im_sdk.GetJoinedGroupList(test, test.OperationID)
-}
-
-type testGetGroupMemberList struct {
- OperationID string
-}
-
-func (t testGetGroupMemberList) OnSuccess(data string) {
- log.ZInfo(ctx, "testGetGroupMemberList", "operationID", t.OperationID, "function", utils.GetSelfFuncName(), "data", data)
-}
-
-func (t testGetGroupMemberList) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, "testGetGroupMemberList", "operationID", t.OperationID, "function", utils.GetSelfFuncName(), "errCode", errCode, "errMsg", errMsg)
-}
-
-func DotestGetGroupMemberList() {
- var test testGetGroupMemberList
- test.OperationID = utils.OperationIDGenerator()
- var groupId = TestgroupID
- open_im_sdk.GetGroupMemberList(test, test.OperationID, groupId, 4, 0, 100)
-}
-
-func DotestCos() {
- //var callback baseCallback
- //p := ws.NewPostApi(token, UserForSDK.ImConfig().ApiAddr)
- //var storage common.ObjectStorage = common.NewCOS(p)
- //test(storage, callback)
-}
-
-//funcation DotestMinio() {
-// var callback baseCallback
-// token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiIxMzkwMDAwMDAwMCIsIlBsYXRmb3JtIjoiSU9TIiwiZXhwIjoxNjQ1NzgyNDY0LCJuYmYiOjE2NDUxNzc2NjQsImlhdCI6MTY0NTE3NzY2NH0.T-SDoLxdlwRGOMZPIKriPtAlOGWCLodsGi1dWxN8kto"
-// p := ws.NewPostApi(token, "https://storage.rentsoft.cn")
-// minio := common.NewMinio(p)
-// var storage common.ObjectStorage = minio
-// log.NewInfo("", *minio)
-// test(storage, callback)
-//}
-//
-//funcation test(storage common.ObjectStorage, callback baseCallback) {
-// dir, newName, err := storage.UploadFile("./cmd/main.go", funcation(progress int) {
-// if progress == 100 {
-// callback.OnSuccess("")
-// }
-// })
-// log.NewInfo("0", dir, newName, err)
-// dir, newName, err = storage.UploadImage("C:\\Users\\Administrator\\Desktop\\1.jpg", funcation(progress int) {
-// if progress == 100 {
-// callback.OnSuccess("")
-// }
-// })
-// log.NewInfo("0", dir, newName, err, err)
-// dir, newName, err = storage.UploadSound("./cmd/main.go", funcation(progress int) {
-// if progress == 100 {
-// callback.OnSuccess("")
-// }
-// })
-// log.NewInfo("0", dir, newName, err, err)
-// snapshotURL, snapshotUUID, videoURL, videoUUID, err := storage.UploadVideo("./cmd/main.go", "C:\\Users\\Administrator\\Desktop\\1.jpg", funcation(progress int) {
-// if progress == 100 {
-// callback.OnSuccess("")
-// }
-// })
-// log.NewInfo(snapshotURL, snapshotUUID, videoURL, videoUUID, err)
-//}
-
-type testGetGroupMembersInfo struct {
-}
-
-func (testGetGroupMembersInfo) OnError(errCode int32, errMsg string) {
- fmt.Println("testGetGroupMembersInfo OnError", errCode, errMsg)
-}
-
-func (testGetGroupMembersInfo) OnSuccess(data string) {
- fmt.Println("testGetGroupMembersInfo OnSuccess, output", data)
-}
-
-//
-//funcation DotestGetGroupMembersInfo() {
-// var test testGetGroupMembersInfo
-// var memlist []string
-// memlist = append(memlist, "307edc814bb0d04a")
-// //memlist = append(memlist, "ded01dfe543700402608e19d4e2f839e")
-// jlist, _ := json.Marshal(memlist)
-// fmt.Println("GetGroupMembersInfo input : ", string(jlist))
-// sdk_interface.GetGroupMembersInfo("7ff61d8f9d4a8a0d6d70a14e2683aad5", string(jlist), test)
-// //GetGroupMemberList("05dc84b52829e82242a710ecf999c72c", 0, 0, test)
-//}
-//
-
-type baseCallback struct {
- OperationID string
- callName string
-}
-
-func (t baseCallback) OnSuccess(data string) {
- log.ZInfo(ctx, t.callName, "operationID", t.OperationID, "function", utils.GetSelfFuncName(), "data", data)
-}
-
-func (t baseCallback) OnError(errCode int32, errMsg string) {
- log.ZInfo(ctx, t.callName, "operationID", t.OperationID, "function", utils.GetSelfFuncName(), "errCode", errCode, "errMsg", errMsg)
-}
-
-type testKickGroupMember struct {
- baseCallback
-}
-type testGetGroupMemberListByJoinTimeFilter struct {
- baseCallback
-}
-
-func DotestGetGroupMemberListByJoinTimeFilter() {
- var test testGetGroupMemberListByJoinTimeFilter
- test.OperationID = utils.OperationIDGenerator()
- var memlist []string
- jlist := utils.StructToJsonString(memlist)
- log.ZInfo(ctx, "DotestGetGroupMemberListByJoinTimeFilter", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", jlist)
- open_im_sdk.GetGroupMemberListByJoinTimeFilter(test, test.OperationID, "3750066757", 1, 40, 0, 0, jlist)
-}
-
-func DotestKickGroupMember() {
- var test testKickGroupMember
- test.OperationID = utils.OperationIDGenerator()
- var memlist []string
- memlist = append(memlist, MemberUserID)
- jlist := utils.StructToJsonString(memlist)
- log.ZInfo(ctx, "DotestKickGroupMember", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", jlist)
- open_im_sdk.KickGroupMember(test, test.OperationID, TestgroupID, "kkk", jlist)
-}
-
-type testInviteUserToGroup struct {
- baseCallback
-}
-
-func DotestInviteUserToGroup() {
- var test testInviteUserToGroup
- test.OperationID = utils.OperationIDGenerator()
- var memlist []string
- memlist = append(memlist, MemberUserID)
- jlist := utils.StructToJsonString(memlist)
- log.ZInfo(ctx, "DotestInviteUserToGroup", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", jlist)
- open_im_sdk.InviteUserToGroup(test, test.OperationID, TestgroupID, "come", string(jlist))
-}
-
-type testGetGroupApplicationList struct {
- baseCallback
-}
-
-func DotestGetRecvGroupApplicationList() string {
- var test testGetGroupApplicationList
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DotestGetRecvGroupApplicationList", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- // open_im_sdk.GetRecvGroupApplicationList(test, test.OperationID)
- return ""
-}
-
-// funcation DoGroupApplicationList() {
-// var test testGroupX
-// fmt.Println("test DoGetGroupApplicationList....")
-// sdk_interface.GetGroupApplicationList(test)
-// }
-type testTransferGroupOwner struct {
- baseCallback
-}
-
-func DotestTransferGroupOwner() {
- var test testTransferGroupOwner
- test.OperationID = utils.OperationIDGenerator()
-
- open_im_sdk.TransferGroupOwner(test, test.OperationID, TestgroupID, MemberUserID)
-
-}
-
-type testProcessGroupApplication struct {
- baseCallback
-}
-
-func DoTestAcceptGroupApplication(uid string) {
- var test testProcessGroupApplication
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DoTestAcceptGroupApplication", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- open_im_sdk.AcceptGroupApplication(test, test.OperationID, TestgroupID, MemberUserID, "ok")
-}
-
-func DoTestGetUserReqGroupApplicationList() {
- var test testProcessGroupApplication
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DoTestGetUserReqGroupApplicationList", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- // open_im_sdk.GetSendGroupApplicationList(test, test.OperationID)
-}
-
-func DoTestGetRecvGroupApplicationList() {
- var test testProcessGroupApplication
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DoTestGetRecvGroupApplicationList", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- // open_im_sdk.GetRecvGroupApplicationList(test, test.OperationID)
-}
-
-func DotestRefuseGroupApplication(uid string) {
- var test testProcessGroupApplication
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DotestRefuseGroupApplication", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- open_im_sdk.RefuseGroupApplication(test, test.OperationID, TestgroupID, MemberUserID, "no")
-}
-
-type testSetGroupMemberNickname struct {
- baseCallback
-}
-
-func DotestSetGroupMemberNickname(myUserID string) {
- var test testSetGroupMemberNickname
- test.OperationID = utils.OperationIDGenerator()
- log.ZInfo(ctx, "DotestSetGroupMemberNickname", "operationID", test.OperationID, "function", utils.GetSelfFuncName(), "input", "")
- open_im_sdk.SetGroupMemberNickname(test, test.OperationID, TestgroupID, myUserID, "")
-}
-
-func DoTestSetGroupMemberRoleLevel(groupID, userID string, roleLevel int) {
- var test testSetGroupMemberNickname
- test.OperationID = utils.OperationIDGenerator()
- fmt.Println(test.OperationID, utils.GetSelfFuncName(), "inputx: ")
- open_im_sdk.SetGroupMemberRoleLevel(test, test.OperationID, groupID, userID, roleLevel)
-}
-
-func DoTestSetGroupMemberInfo(groupID, userID string, ex string) {
- var test testSetGroupMemberNickname
- test.OperationID = utils.OperationIDGenerator()
- param := sdk_params_callback.SetGroupMemberInfoParam{GroupID: groupID, UserID: userID}
- if ex != "" {
- param.Ex = &ex
- }
- g1 := utils.StructToJsonString(param)
- fmt.Println(test.OperationID, utils.GetSelfFuncName(), "inputx: ", g1)
-
- open_im_sdk.SetGroupMemberInfo(test, test.OperationID, g1)
-}
diff --git a/test/t_signaling.go b/test/t_signaling.go
deleted file mode 100644
index 052bf1ddf..000000000
--- a/test/t_signaling.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
-
- "github.com/openimsdk/tools/log"
- "golang.org/x/net/context"
-)
-
-type testSignalingListener struct {
- ctx context.Context
-}
-
-func (s *testSignalingListener) OnHangUp(hangUpCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnHangUp ", hangUpCallback)
-}
-
-func (s *testSignalingListener) OnReceiveNewInvitation(receiveNewInvitationCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnReceiveNewInvitation ", receiveNewInvitationCallback)
-}
-
-func (s *testSignalingListener) OnInviteeAccepted(inviteeAcceptedCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInviteeAccepted ", inviteeAcceptedCallback)
-}
-
-func (s *testSignalingListener) OnInviteeRejected(inviteeRejectedCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInviteeRejected ", inviteeRejectedCallback)
-}
-
-func (s *testSignalingListener) OnInvitationCancelled(invitationCancelledCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInvitationCancelled ", invitationCancelledCallback)
-}
-
-func (s *testSignalingListener) OnInvitationTimeout(invitationTimeoutCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInvitationTimeout ", invitationTimeoutCallback)
-}
-
-func (s *testSignalingListener) OnInviteeAcceptedByOtherDevice(inviteeAcceptedCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInviteeAcceptedByOtherDevice ", inviteeAcceptedCallback)
-}
-
-func (s *testSignalingListener) OnInviteeRejectedByOtherDevice(inviteeRejectedCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "OnInviteeRejectedByOtherDevice ", inviteeRejectedCallback)
-}
-
-func (s *testSignalingListener) OnRoomParticipantConnected(onRoomChangeCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "onRoomChangeCallback", onRoomChangeCallback)
-}
-
-func (s *testSignalingListener) OnRoomParticipantDisconnected(onRoomChangeCallback string) {
- log.ZInfo(s.ctx, utils.GetSelfFuncName(), "onRoomChangeCallback", onRoomChangeCallback)
-}
-
-//type testSingaling struct {
-// baseCallback
-//}
-//
-//funcation DoTestInviteInGroup() {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalInviteInGroupReq{}
-// req.Invitation = SetTestInviteInfo()
-// s := utils.StructToJsonString(req)
-// // log.Info(t.OperationID, utils.GetSelfFuncName(), "input: ", s)
-// open_im_sdk.SignalingInviteInGroup(t, t.OperationID, s)
-//}
-//
-//funcation SetTestInviteInfo() *sdkws.InvitationInfo {
-// req := &sdkws.InvitationInfo{}
-// req.Timeout = 1000
-// req.InviteeUserIDList = []string{"3495023045"}
-// req.MediaType = "video"
-// req.RoomID = "1826384574"
-// req.GroupID = "1826384574"
-// req.SessionType = 2
-// return req
-//}
-//
-//funcation DoTestInvite(userID string) {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalInviteReq{}
-// req.OpUserID = userID
-// req.Invitation = SetTestInviteInfo()
-// req.Invitation.GroupID = ""
-// req.Invitation.SessionType = 1
-// req.Invitation.PlatformID = 1
-// req.Invitation.Timeout = 30
-// req.Invitation.MediaType = "video"
-// req.Invitation.InviteeUserIDList = []string{"17726378428"}
-// s := utils.StructToJsonString(req)
-// fmt.Println(utils.GetSelfFuncName(), "input: ", s, t.OperationID)
-// open_im_sdk.SignalingInvite(t, t.OperationID, s)
-//}
-//
-//funcation DoTestAccept() {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalAcceptReq{Invitation: &sdkws.InvitationInfo{}, OpUserID: "18349115126"}
-// req.Invitation = SetTestInviteInfo()
-// req.Invitation.InviterUserID = "18666662412"
-// s := utils.StructToJsonString(req)
-// // log.Info(t.OperationID, utils.GetSelfFuncName(), "input: ", s, req.String())
-// open_im_sdk.SignalingAccept(t, t.OperationID, s)
-//}
-//
-//funcation DoTestReject() {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalRejectReq{Invitation: &sdkws.InvitationInfo{}, OpUserID: "18349115126"}
-// req.Invitation = SetTestInviteInfo()
-// req.Invitation.InviterUserID = "18666662412"
-// s := utils.StructToJsonString(req)
-// // log.Info(t.OperationID, utils.GetSelfFuncName(), "input: ", s)
-// open_im_sdk.SignalingReject(t, t.OperationID, s)
-//}
-//
-//funcation DoTestCancel() {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalCancelReq{Invitation: &sdkws.InvitationInfo{}}
-// req.Invitation = SetTestInviteInfo()
-// req.Invitation.GroupID = ""
-// req.Invitation.SessionType = 1
-// req.Invitation.PlatformID = 1
-// req.Invitation.Timeout = 10
-// req.Invitation.InviterUserID = "18666662412"
-// req.OpUserID = "18666662412"
-// s := utils.StructToJsonString(req)
-// // log.Info(t.OperationID, utils.GetSelfFuncName(), "input: ", s)
-// open_im_sdk.SignalingCancel(t, t.OperationID, s)
-//}
-//
-//funcation DoTestHungUp() {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// req := &sdkws.SignalHungUpReq{Invitation: &sdkws.InvitationInfo{}}
-// req.Invitation = SetTestInviteInfo()
-// s := utils.StructToJsonString(req)
-// // log.Info(t.OperationID, utils.GetSelfFuncName(), "input: ", s)
-// open_im_sdk.SignalingHungUp(t, t.OperationID, s)
-//}
-//
-//funcation DoTestSignalGetRoomByGroupID(groupID string) {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// open_im_sdk.SignalingGetRoomByGroupID(t, t.OperationID, groupID)
-//}
-//
-//funcation DoTestSignalGetTokenByRoomID(roomID string) {
-// t := testSingaling{baseCallback{OperationID: utils.OperationIDGenerator(), callName: utils.GetSelfFuncName()}}
-// open_im_sdk.SignalingGetTokenByRoomID(t, t.OperationID, roomID)
-//}
diff --git a/test/third_test.go b/test/third_test.go
new file mode 100644
index 000000000..d12107119
--- /dev/null
+++ b/test/third_test.go
@@ -0,0 +1,28 @@
+package test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
+)
+
+type SProgress struct{}
+
+func (s SProgress) OnProgress(current int64, size int64) {
+
+}
+
+func Test_UploadLog(t *testing.T) {
+ tm := time.Now()
+ err := open_im_sdk.UserForSDK.Third().UploadLogs(ctx, 2000, "it is ex", SProgress{})
+ if err != nil {
+ t.Error(err)
+ }
+ fmt.Println(time.Since(tm).Microseconds())
+
+}
+func Test_SDKLogs(t *testing.T) {
+ open_im_sdk.UserForSDK.Third().Log(ctx, 4, "cmd/abc.go", 666, "This is a test message", "", []any{"key", "value"})
+}
diff --git a/testv2/user_test.go b/test/user_test.go
similarity index 50%
rename from testv2/user_test.go
rename to test/user_test.go
index 229928f76..4612f53e8 100644
--- a/testv2/user_test.go
+++ b/test/user_test.go
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testv2
+package test
import (
"fmt"
- "github.com/openimsdk/protocol/user"
- "github.com/openimsdk/protocol/wrapperspb"
"testing"
"time"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/protocol/wrapperspb"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/protocol/sdkws"
@@ -35,62 +36,10 @@ func Test_GetSelfUserInfo(t *testing.T) {
t.Log(userInfo)
}
-func Test_GetUsersInfo(t *testing.T) {
- userInfo, err := open_im_sdk.UserForSDK.Full().GetUsersInfo(ctx, []string{"friendUserID"})
- if err != nil {
- t.Error(err)
- }
- if userInfo[0].BlackInfo != nil {
- t.Log(userInfo[0].BlackInfo)
- }
- if userInfo[0].FriendInfo != nil {
- t.Log(userInfo[0].FriendInfo)
- }
- if userInfo[0].PublicInfo != nil {
- t.Log(userInfo[0].PublicInfo)
- }
-}
-func Test_GetUsersInfoWithCache(t *testing.T) {
- userInfo, err := open_im_sdk.UserForSDK.Full().GetUsersInfoWithCache(ctx, []string{"1"}, "")
- if err != nil {
- t.Error(err)
- }
- if userInfo[0].BlackInfo != nil {
- t.Log(userInfo[0].BlackInfo)
- }
- if userInfo[0].FriendInfo != nil {
- t.Log(userInfo[0].FriendInfo)
- }
- if userInfo[0].PublicInfo != nil {
- t.Log(userInfo[0].PublicInfo)
- }
-}
-func Test_SetSelfInfo(t *testing.T) {
- newNickName := "test"
- //newFaceURL := "http://test.com"
- err := open_im_sdk.UserForSDK.User().SetSelfInfo(ctx, &sdkws.UserInfo{
- Nickname: newNickName,
- //FaceURL: newFaceURL,
- })
- newFaceURL := "http://test.com"
-
- if err != nil {
- t.Error(err)
- }
- userInfo, err := open_im_sdk.UserForSDK.User().GetSelfUserInfo(ctx)
- if err != nil {
- t.Error(err)
- }
- if userInfo.UserID != UserID && userInfo.Nickname != newNickName && userInfo.FaceURL != newFaceURL {
- t.Error("user id not match")
- }
- t.Log(userInfo)
- time.Sleep(time.Second * 10)
-}
func Test_SetSelfInfoEx(t *testing.T) {
newNickName := "test"
//newFaceURL := "http://test.com"
- err := open_im_sdk.UserForSDK.User().SetSelfInfoEx(ctx, &sdkws.UserInfoWithEx{
+ err := open_im_sdk.UserForSDK.User().SetSelfInfo(ctx, &sdkws.UserInfoWithEx{
Nickname: &wrapperspb.StringValue{
Value: newNickName,
},
@@ -115,68 +64,6 @@ func Test_SetSelfInfoEx(t *testing.T) {
time.Sleep(time.Second * 10)
}
-func Test_UpdateMsgSenderInfo(t *testing.T) {
- err := open_im_sdk.UserForSDK.User().UpdateMsgSenderInfo(ctx, "test", "http://test.com")
- if err != nil {
- t.Error(err)
- }
- userInfo, err := open_im_sdk.UserForSDK.User().GetSelfUserInfo(ctx)
- if err != nil {
- t.Error(err)
- }
- t.Log(userInfo)
-}
-
-func Test_SetSetGlobalRecvMessageOpt(t *testing.T) {
- err := open_im_sdk.UserForSDK.User().SetGlobalRecvMessageOpt(ctx, 1)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_Sub(t *testing.T) {
- var users []string
- users = append(users, "2926672950")
- status, err := open_im_sdk.UserForSDK.User().SubscribeUsersStatus(ctx, users)
- if err != nil {
- t.Error(err)
- }
- t.Log(status)
-
- for i := 0; i < 20; i++ {
- status, err = open_im_sdk.UserForSDK.User().SubscribeUsersStatus(ctx, users)
- t.Log(status)
- time.Sleep(time.Second * 3)
- }
-}
-
-func Test_GetSubscribeUsersStatus(t *testing.T) {
- status, err := open_im_sdk.UserForSDK.User().GetSubscribeUsersStatus(ctx)
- if err != nil {
- return
- }
- t.Log(status)
-}
-
-func Test_GetUserStatus(t *testing.T) {
- var UserIDs []string
- UserIDs = append(UserIDs, "2926672950")
- status, err := open_im_sdk.UserForSDK.User().GetUserStatus(ctx, UserIDs)
- if err != nil {
- return
- }
- t.Log(status)
-}
-
-func Test_UnSub(t *testing.T) {
- var users []string
- users = append(users, "2926672950")
- err := open_im_sdk.UserForSDK.User().UnsubscribeUsersStatus(ctx, users)
- if err != nil {
- t.Error(err)
- }
-}
-
func Test_UserCommandAdd(t *testing.T) {
// Creating a request with a pointer
req := &user.ProcessUserCommandAddReq{
diff --git a/test/work_group_create.go b/test/work_group_create.go
deleted file mode 100644
index 4bcca7958..000000000
--- a/test/work_group_create.go
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "encoding/json"
- "errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/network"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/tools/log"
- "sync"
- "time"
-
- "github.com/openimsdk/protocol/sdkws"
-)
-
-var (
- INVITEUSERTOGROUP = ""
-)
-
-func InviteListToGroup(userIDList []string, groupID string) {
- var inviteReq server_api_params.InviteUserToGroupReq
- inviteReq.OperationID = utils.OperationIDGenerator()
- inviteReq.GroupID = groupID
- inviteReq.InvitedUserIDList = userIDList
- for {
- resp, err := network.Post2Api(INVITEUSERTOGROUP, inviteReq, AdminToken)
- if err != nil {
- log.ZWarn(ctx, "INVITE USER TO GROUP failed", err, "inviteReq", inviteReq)
-
- continue
- } else {
- log.ZInfo(ctx, "InviteResponse", "operationID", inviteReq.OperationID, "response", string(resp))
-
- return
- }
- }
-}
-
-func InviteToGroup(userID string, groupID string) {
- var inviteReq server_api_params.InviteUserToGroupReq
- inviteReq.OperationID = utils.OperationIDGenerator()
- inviteReq.GroupID = groupID
- inviteReq.InvitedUserIDList = []string{userID}
- for {
- resp, err := network.Post2Api(INVITEUSERTOGROUP, inviteReq, AdminToken)
- if err != nil {
- log.ZWarn(ctx, "INVITE USER TO GROUP failed", err, "inviteReq", inviteReq)
-
- continue
- } else {
- log.ZInfo(ctx, "invite resp", "operationID", inviteReq.OperationID, "response", string(resp))
-
- return
- }
- }
-}
-
-func CreateWorkGroup(number int) string {
- t1 := time.Now()
- RegisterWorkGroupAccounts(number)
- log.ZInfo(ctx, "RegisterAccounts", "costTime", time.Since(t1), "onlineClientNumber", number)
-
- groupID := ""
- var req server_api_params.CreateGroupReq
-
- //var memberList []*server_api_params.GroupAddMemberInfo
- //for _, v := range allUserID {
- // memberList = append(memberList, &server_api_params.GroupAddMemberInfo{UserID: v, RoleLevel: 1})
- //}
- // req.MemberList = memberList
- req.OwnerUserID = "openIM123456"
- for {
- req.OperationID = utils.OperationIDGenerator()
- req.GroupType = constant.WorkingGroup
- req.OperationID = utils.OperationIDGenerator()
- resp, err := network.Post2Api(CREATEGROUP, req, AdminToken)
- if err != nil {
- log.ZWarn(ctx, "CREATE GROUP failed", err, "resp", resp)
- continue
- } else {
- type CreateGroupResp struct {
- server_api_params.CommResp
- GroupInfo sdkws.GroupInfo `json:"data"`
- }
-
- var result CreateGroupResp
- err := json.Unmarshal(resp, &result)
- if err != nil {
- log.ZError(ctx, "Unmarshal failed", err, "resp", string(resp))
-
- }
- log.ZInfo(ctx, "Unmarshal", "operationID", req.OperationID, "response", string(resp), "result", result)
- groupID = result.GroupInfo.GroupID
- log.ZInfo(ctx, "create groupID", "operationID", req.OperationID, "groupID", groupID)
-
- break
- }
- }
-
- split := 100
- idx := 0
- remain := len(allUserID) % split
- for idx = 0; idx < len(allUserID)/split; idx++ {
- sub := allUserID[idx*split : (idx+1)*split]
- log.ZWarn(ctx, "Invite to groupID", errors.New(""), "groupID", groupID)
-
- InviteListToGroup(sub, groupID)
- }
- if remain > 0 {
- sub := allUserID[idx*split:]
- log.ZWarn(ctx, "Invite to groupID", errors.New(""), "operationID", req.OperationID, "groupID", groupID)
-
- InviteListToGroup(sub, groupID)
- }
-
- //var wg sync.WaitGroup
- //for _, v := range allUserID {
- // wg.Add(1)
- // go funcation(uID, gID string) {
- // InviteToGroup(uID, gID)
- // wg.Done()
- // }(v, groupID)
- //}
- //wg.Wait()
- return groupID
-}
-
-func RegisterWorkGroupAccounts(number int) {
- var wg sync.WaitGroup
- wg.Add(number)
- for i := 0; i < number; i++ {
- go func(t int) {
- userID := GenUid(t, "workgroup")
- register(userID)
- log.ZInfo(ctx, "UserRegistered", "userID", userID)
- wg.Done()
- }(i)
- }
- wg.Wait()
-
- log.ZInfo(ctx, "RegistrationFinished", "totalUsers", number)
-}
-
-func RegisterWorkGroupPressAccounts(number int) {
- var wg sync.WaitGroup
- wg.Add(number)
- for i := 0; i < number; i++ {
- go func(t int) {
- userID := GenUid(t, "press_workgroup")
- register(userID)
- log.ZInfo(ctx, "UserRegistered", "userID", userID)
- wg.Done()
- }(i)
- }
- wg.Wait()
-
- userID1 := GenUid(1234567, "workgroup")
- register(userID1)
- userID2 := GenUid(7654321, "workgroup")
- register(userID2)
- log.ZInfo(ctx, "RegistrationFinished", "totalUsers", number+2)
-}
diff --git a/test/work_moments.go b/test/work_moments.go
deleted file mode 100644
index a85d56bda..000000000
--- a/test/work_moments.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package test
-
-import (
- "fmt"
-)
-
-type WBase struct {
-}
-
-func (WBase) OnError(errCode int32, errMsg string) {
- fmt.Println("get workmoments OnError", errCode, errMsg)
-}
-func (WBase) OnSuccess(data string) {
- fmt.Println("get workmoments OnSuccess, ", data)
-}
-
-func (WBase) OnProgress(progress int) {
- fmt.Println("OnProgress, ", progress)
-}
-
-//funcation TestGetWorkMomentsUnReadCount() {
-// operationID := utils.OperationIDGenerator()
-// var cb WBase
-// open_im_sdk.GetWorkMomentsUnReadCount(cb, operationID)
-//}
-//
-//funcation TestGetWorkMomentsNotification() {
-// operationID := utils.OperationIDGenerator()
-// var cb WBase
-// offset := 0
-// count := 10
-// open_im_sdk.GetWorkMomentsNotification(cb, operationID, offset, count)
-//}
-//
-//funcation TestClearWorkMomentsNotification() {
-// operationID := utils.OperationIDGenerator()
-// var cb WBase
-// open_im_sdk.ClearWorkMomentsNotification(cb, operationID)
-//}
diff --git a/testv2/config.go b/testv2/config.go
deleted file mode 100644
index 073dce8c4..000000000
--- a/testv2/config.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
- "github.com/openimsdk/protocol/constant"
-)
-
-const (
- //APIADDR = "http://172.16.8.48:10002"
- //WSADDR = "ws://172.16.8.48:10001"
-
- APIADDR = "http://127.0.0.1:10002"
- WSADDR = "ws://127.0.0.1:10001"
-
- UserID = "6864873394"
- friendUserID = "3281432310"
-)
-
-func getConf(APIADDR, WSADDR string) sdk_struct.IMConfig {
- var cf sdk_struct.IMConfig
- cf.ApiAddr = APIADDR
- cf.WsAddr = WSADDR
- cf.DataDir = "../"
- cf.LogLevel = 6
- cf.IsExternalExtensions = true
- cf.PlatformID = constant.LinuxPlatformID
- cf.LogFilePath = ""
- cf.IsLogStandardOutput = true
- return cf
-}
diff --git a/testv2/friend_test.go b/testv2/friend_test.go
deleted file mode 100644
index 48292c6cb..000000000
--- a/testv2/friend_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
- "github.com/openimsdk/protocol/wrapperspb"
- "testing"
- "time"
-
- friend2 "github.com/openimsdk/protocol/relation"
-)
-
-func Test_GetSpecifiedFriendsInfo(t *testing.T) {
- info, err := open_im_sdk.UserForSDK.Friend().GetSpecifiedFriendsInfo(ctx, []string{"45644221123"})
- if err != nil {
- t.Fatal(err)
- }
- t.Log("GetDesignatedFriendsInfo success", ctx.Value("operationID"))
- for _, userInfo := range info {
- t.Log(userInfo)
- }
-}
-
-func Test_AddFriend(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().AddFriend(ctx, &friend2.ApplyToAddFriendReq{
- ToUserID: "2",
- ReqMsg: "test add",
- Ex: "add",
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Log("AddFriend success", ctx.Value("operationID"))
-}
-
-//funcation Test_GetRecvFriendApplicationList(t *testing.T) {
-// infos, err := open_im_sdk.UserForSDK.Friend().GetRecvFriendApplicationList(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-// for _, info := range infos {
-// t.Logf("%#v", info)
-// }
-//}
-//
-//funcation Test_GetSendFriendApplicationList(t *testing.T) {
-// infos, err := open_im_sdk.UserForSDK.Friend().GetSendFriendApplicationList(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-// for _, info := range infos {
-// t.Logf("%#v", info)
-// }
-//}
-
-func Test_AcceptFriendApplication(t *testing.T) {
- req := &sdk_params_callback.ProcessFriendApplicationParams{ToUserID: "1", HandleMsg: "test accept"}
- err := open_im_sdk.UserForSDK.Friend().AcceptFriendApplication(ctx, req)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("AcceptFriendApplication success", ctx.Value("operationID"))
- time.Sleep(time.Second * 30)
-}
-
-func Test_RefuseFriendApplication(t *testing.T) {
- req := &sdk_params_callback.ProcessFriendApplicationParams{ToUserID: "6754269405", HandleMsg: "test refuse"}
- err := open_im_sdk.UserForSDK.Friend().RefuseFriendApplication(ctx, req)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("RefuseFriendApplication success", ctx.Value("operationID"))
- time.Sleep(time.Second * 30)
-}
-
-func Test_CheckFriend(t *testing.T) {
- res, err := open_im_sdk.UserForSDK.Friend().CheckFriend(ctx, []string{"863454357", "45644221123"})
- if err != nil {
- t.Fatal(err)
- }
- t.Log("CheckFriend success", ctx.Value("operationID"))
- for _, re := range res {
- t.Log(re)
- }
-}
-func Test_PinFriend(t *testing.T) {
- pinParams := &sdk_params_callback.SetFriendPinParams{
- ToUserIDs: []string{"2", "3"},
- IsPinned: &wrapperspb.BoolValue{Value: false},
- }
-
- err := open_im_sdk.UserForSDK.Friend().PinFriends(ctx, pinParams)
-
- if err != nil {
- t.Fatal(err)
- }
- t.Log("CheckFriend success", ctx.Value("operationID"))
-}
-func Test_DeleteFriend(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().DeleteFriend(ctx, "863454357")
- if err != nil {
- t.Fatal(err)
- }
- t.Log("DeleteFriend success", ctx.Value("operationID"))
-}
-
-func Test_GetFriendList(t *testing.T) {
- infos, err := open_im_sdk.UserForSDK.Friend().GetFriendList(ctx)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("GetFriendList success", ctx.Value("operationID"))
- for _, info := range infos {
- t.Logf("PublicInfo: %#v, FriendInfo: %#v, BlackInfo: %#v", info.PublicInfo, info.FriendInfo, info.BlackInfo)
- }
-}
-
-func Test_SearchFriends(t *testing.T) {
- info, err := open_im_sdk.UserForSDK.Friend().SearchFriends(ctx, &sdk_params_callback.SearchFriendsParam{KeywordList: []string{"863454357"}, IsSearchUserID: true})
- if err != nil {
- t.Fatal(err)
- }
- t.Log("SearchFriends success", ctx.Value("operationID"))
- for _, item := range info {
- t.Log(*item)
- }
-}
-
-func Test_SetFriendRemark(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SetFriendRemark(ctx, &sdk_params_callback.SetFriendRemarkParams{ToUserID: "863454357", Remark: "testRemark"})
- if err != nil {
- t.Fatal(err)
- }
- t.Log("SetFriendRemark success", ctx.Value("operationID"))
-}
-
-func Test_AddBlack(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().AddBlack(ctx, "863454357", "ex")
- if err != nil {
- t.Fatal(err)
- }
- t.Log("AddBlack success", ctx.Value("operationID"))
-}
-
-func Test_RemoveBlack(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().RemoveBlack(ctx, "863454357")
- if err != nil {
- t.Fatal(err)
- }
- t.Log("RemoveBlack success", ctx.Value("operationID"))
-}
-
-func Test_GetBlackList(t *testing.T) {
- info, err := open_im_sdk.UserForSDK.Friend().GetBlackList(ctx)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("GetBlackList success", ctx.Value("operationID"))
- for _, item := range info {
- t.Log(*item)
- }
-}
-func Test_SetFriendsEx(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SetFriendsEx(ctx, []string{"2"}, "exx")
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/testv2/listener.go b/testv2/listener.go
deleted file mode 100644
index 085552304..000000000
--- a/testv2/listener.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-type OnConnListener struct{}
-
-func (c *OnConnListener) OnUserTokenInvalid(errMsg string) {}
-
-func (c *OnConnListener) OnConnecting() {
- // fmt.Println("OnConnecting")
-}
-
-func (c *OnConnListener) OnConnectSuccess() {
- // fmt.Println("OnConnectSuccess")
-}
-
-func (c *OnConnListener) OnConnectFailed(errCode int32, errMsg string) {
- // fmt.Println("OnConnectFailed")
-}
-
-func (c *OnConnListener) OnKickedOffline() {
- // fmt.Println("OnKickedOffline")
-}
-
-func (c *OnConnListener) OnUserTokenExpired() {
- // fmt.Println("OnUserTokenExpired")
-}
diff --git a/testv2/signaling_test.go b/testv2/signaling_test.go
deleted file mode 100644
index e741cc53f..000000000
--- a/testv2/signaling_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-// func Test_SignalingInviteInGroup(t *testing.T) {
-// resp, err := open_im_sdk.UserForSDK.Signaling().SignalingInviteInGroup(ctx, &sdkws.SignalInviteInGroupReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "testgroup",
-// RoomID: "testgroup",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 3,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(resp)
-// }
-
-// func Test_SignalingInite(t *testing.T) {
-// resp, err := open_im_sdk.UserForSDK.Signaling().SignalingInvite(ctx, &sdkws.SignalInviteReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "",
-// RoomID: "testroomID",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 1,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(resp)
-// }
-
-// func Test_SignalingAccept(t *testing.T) {
-// resp, err := open_im_sdk.UserForSDK.Signaling().SignalingAccept(ctx, &sdkws.SignalAcceptReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "",
-// RoomID: "testroomID",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 1,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(resp)
-// }
-
-// func Test_SignalingReject(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Signaling().SignalingReject(ctx, &sdkws.SignalRejectReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "",
-// RoomID: "testroomID",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 1,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// }
-
-// func Test_SignalingCancel(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Signaling().SignalingCancel(ctx, &sdkws.SignalCancelReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "",
-// RoomID: "testroomID",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 1,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// }
-
-// func Test_SignalingHungUp(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Signaling().SignalingHungUp(ctx, &sdkws.SignalHungUpReq{
-// Invitation: &sdkws.InvitationInfo{
-// InviterUserID: UserID,
-// InviteeUserIDList: []string{"targetUser"},
-// CustomData: "",
-// GroupID: "",
-// RoomID: "testroomID",
-// Timeout: 30,
-// MediaType: "video",
-// PlatformID: 1,
-// SessionType: 1,
-// },
-// })
-// if err != nil {
-// t.Error(err)
-// }
-// }
-
-// func Test_SignalingGetRoomByGroupID(t *testing.T) {
-// resp, err := open_im_sdk.UserForSDK.Signaling().SignalingGetRoomByGroupID(ctx, "testgroupID")
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(resp)
-// }
-
-// func Test_SignalingGetTokenByRoomID(t *testing.T) {
-// resp, err := open_im_sdk.UserForSDK.Signaling().SignalingGetTokenByRoomID(ctx, "testroomID")
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(resp)
-// }
diff --git a/testv2/sync2_test.go b/testv2/sync2_test.go
deleted file mode 100644
index 16e9a0b19..000000000
--- a/testv2/sync2_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-import (
- "fmt"
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "github.com/openimsdk/protocol/constant"
- "github.com/openimsdk/protocol/group"
- "github.com/openimsdk/protocol/sdkws"
- "github.com/openimsdk/tools/log"
- "testing"
- "time"
-)
-
-func TestSyncFriend2(t *testing.T) {
- for i := 0; ; i++ {
- if err := open_im_sdk.UserForSDK.Friend().IncrSyncFriends(ctx); err != nil {
- t.Log("IncrSyncFriends error-->", err)
- continue
- }
- time.Sleep(time.Second)
- }
-}
-
-func TestSyncJoinGroup2(t *testing.T) {
- for i := 0; ; i++ {
- if err := open_im_sdk.UserForSDK.Group().IncrSyncJoinGroup(ctx); err != nil {
- t.Log("IncrSyncJoinGroup error-->", err)
- continue
- }
- time.Sleep(time.Second)
- }
-}
-
-func TestSyncGroupMember2(t *testing.T) {
- for i := 0; ; i++ {
- if err := open_im_sdk.UserForSDK.Group().IncrSyncJoinGroupMember(ctx); err != nil {
- t.Log("IncrSyncGroupAndMember error-->", err)
- continue
- }
- time.Sleep(time.Second)
- }
-}
-
-func TestName(t *testing.T) {
- for i := 1; i <= 600; i++ {
- _, err := open_im_sdk.UserForSDK.Group().CreateGroup(ctx, &group.CreateGroupReq{
- GroupInfo: &sdkws.GroupInfo{
- GroupType: constant.WorkingGroup,
- GroupName: fmt.Sprintf("group_%d", i),
- },
- MemberUserIDs: []string{"9556972319", "9719689061", "3872159645"},
- })
- if err != nil {
- log.ZError(ctx, "group create failed", err, "index", i)
- }
- }
-}
diff --git a/testv2/sync_test.go b/testv2/sync_test.go
deleted file mode 100644
index c0b3663f8..000000000
--- a/testv2/sync_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testv2
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
- "testing"
-)
-
-//func Test_SyncJoinGroup(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Group().SyncJoinedGroup(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-func Test_SyncGroupMember(t *testing.T) {
- //groups, err := open_im_sdk.UserForSDK.Group().GetServerJoinGroup(ctx)
- //if err != nil {
- // t.Fatal(err)
- //}
- //for _, group := range groups {
- // err := open_im_sdk.UserForSDK.Group().SyncGroupMember(ctx, group.GroupID)
- // if err != nil {
- // t.Fatal(err)
- // }
- //}
-}
-
-//func Test_SyncSelfGroupApplication(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Group().SyncSelfGroupApplication(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func Test_SyncAdminGroupApplication(t *testing.T) {
-// err := open_im_sdk.UserForSDK.Group().SyncAdminGroupApplication(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-func Test_SyncSelfFriendApplication(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SyncAllSelfFriendApplication(ctx)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SyncFriendApplication(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SyncAllFriendApplication(ctx)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SyncFriend(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SyncAllFriendList(ctx)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SyncBlack(t *testing.T) {
- err := open_im_sdk.UserForSDK.Friend().SyncAllBlackList(ctx)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_SyncAllConversation(t *testing.T) {
- err := open_im_sdk.UserForSDK.Conversation().SyncAllConversations(ctx)
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/testv2/work_moment_test.go b/testv2/work_moment_test.go
deleted file mode 100644
index 15b242522..000000000
--- a/testv2/work_moment_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package testv2
-
-//
-//import (
-// "open_im_sdk/open_im_sdk"
-// "testing"
-//)
-//
-//funcation Test_GetWorkMomentsUnReadCount(t *testing.T) {
-// unreadCount, err := open_im_sdk.UserForSDK.WorkMoments().GetWorkMomentsUnReadCount(ctx)
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(unreadCount)
-//}
-//
-//funcation Test_GetWorkMomentsNotification(t *testing.T) {
-// notifications, err := open_im_sdk.UserForSDK.WorkMoments().GetWorkMomentsNotification(ctx, 0, 10)
-// if err != nil {
-// t.Error(err)
-// }
-// t.Log(notifications)
-//}
-//
-//funcation Test_ClearWorkMomentsNotification(t *testing.T) {
-// err := open_im_sdk.UserForSDK.WorkMoments().ClearWorkMomentsNotification(ctx)
-// if err != nil {
-// t.Error(err)
-// }
-//}
diff --git a/tools/changelog/changelog.go b/tools/changelog/changelog.go
new file mode 100644
index 000000000..82ee42e51
--- /dev/null
+++ b/tools/changelog/changelog.go
@@ -0,0 +1,198 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+)
+
+// You can specify a tag as a command line argument to generate the changelog for a specific version.
+// Example: go run tools/changelog/changelog.go v0.0.33
+// If no tag is provided, the latest release will be used.
+
+// Setting repo owner and repo name by generate changelog
+const (
+ repoOwner = "openimsdk"
+ repoName = "openim-sdk-core"
+)
+
+// GitHubRepo struct represents the repo details.
+type GitHubRepo struct {
+ Owner string
+ Repo string
+ FullChangelog string
+}
+
+// ReleaseData represents the JSON structure for release data.
+type ReleaseData struct {
+ TagName string `json:"tag_name"`
+ Body string `json:"body"`
+ HtmlUrl string `json:"html_url"`
+ Published string `json:"published_at"`
+}
+
+// Method to classify and format release notes.
+func (g *GitHubRepo) classifyReleaseNotes(body string) map[string][]string {
+ result := map[string][]string{
+ "feat": {},
+ "fix": {},
+ "chore": {},
+ "refactor": {},
+ "build": {},
+ "other": {},
+ }
+
+ // Regular expression to extract PR number and URL (case insensitive)
+ rePR := regexp.MustCompile(`(?i)in (https://github\.com/[^\s]+/pull/(\d+))`)
+
+ // Split the body into individual lines.
+ lines := strings.Split(body, "\n")
+
+ for _, line := range lines {
+ // Skip lines that contain "deps: Merge"
+ if strings.Contains(strings.ToLower(line), "deps: merge #") {
+ continue
+ }
+
+ // Use a regular expression to extract Full Changelog link and its title (case insensitive).
+ if strings.Contains(strings.ToLower(line), "**full changelog**") {
+ matches := regexp.MustCompile(`(?i)\*\*full changelog\*\*: (https://github\.com/[^\s]+/compare/([^\s]+))`).FindStringSubmatch(line)
+ if len(matches) > 2 {
+ // Format the Full Changelog link with title
+ g.FullChangelog = fmt.Sprintf("[%s](%s)", matches[2], matches[1])
+ }
+ continue // Skip further processing for this line.
+ }
+
+ if strings.HasPrefix(line, "*") {
+ var category string
+
+ // Use strings.ToLower to make the matching case insensitive
+ lowerLine := strings.ToLower(line)
+
+ // Determine the category based on the prefix (case insensitive).
+ if strings.HasPrefix(lowerLine, "* feat") {
+ category = "feat"
+ } else if strings.HasPrefix(lowerLine, "* fix") {
+ category = "fix"
+ } else if strings.HasPrefix(lowerLine, "* chore") {
+ category = "chore"
+ } else if strings.HasPrefix(lowerLine, "* refactor") {
+ category = "refactor"
+ } else if strings.HasPrefix(lowerLine, "* build") {
+ category = "build"
+ } else {
+ category = "other"
+ }
+
+ // Extract PR number and URL (case insensitive)
+ matches := rePR.FindStringSubmatch(line)
+ if len(matches) == 3 {
+ prURL := matches[1]
+ prNumber := matches[2]
+ // Format the line with the PR link and use original content for the final result
+ formattedLine := fmt.Sprintf("* %s [#%s](%s)", strings.Split(line, " by ")[0][2:], prNumber, prURL)
+ result[category] = append(result[category], formattedLine)
+ } else {
+ // If no PR link is found, just add the line as is
+ result[category] = append(result[category], line)
+ }
+ }
+ }
+
+ return result
+}
+
+// Method to generate the final changelog.
+func (g *GitHubRepo) generateChangelog(tag, date, htmlURL, body string) string {
+ sections := g.classifyReleaseNotes(body)
+
+ // Convert ISO 8601 date to simpler format (YYYY-MM-DD)
+ formattedDate := date[:10]
+
+ // Changelog header with tag, date, and links.
+ changelog := fmt.Sprintf("## [%s](%s) \t(%s)\n\n", tag, htmlURL, formattedDate)
+
+ if len(sections["feat"]) > 0 {
+ changelog += "### New Features\n" + strings.Join(sections["feat"], "\n") + "\n\n"
+ }
+ if len(sections["fix"]) > 0 {
+ changelog += "### Bug Fixes\n" + strings.Join(sections["fix"], "\n") + "\n\n"
+ }
+ if len(sections["chore"]) > 0 {
+ changelog += "### Chores\n" + strings.Join(sections["chore"], "\n") + "\n\n"
+ }
+ if len(sections["refactor"]) > 0 {
+ changelog += "### Refactors\n" + strings.Join(sections["refactor"], "\n") + "\n\n"
+ }
+ if len(sections["build"]) > 0 {
+ changelog += "### Builds\n" + strings.Join(sections["build"], "\n") + "\n\n"
+ }
+ if len(sections["other"]) > 0 {
+ changelog += "### Others\n" + strings.Join(sections["other"], "\n") + "\n\n"
+ }
+
+ if g.FullChangelog != "" {
+ changelog += fmt.Sprintf("**Full Changelog**: %s\n", g.FullChangelog)
+ }
+
+ return changelog
+}
+
+// Method to fetch release data from GitHub API.
+func (g *GitHubRepo) fetchReleaseData(version string) (*ReleaseData, error) {
+ var apiURL string
+
+ if version == "" {
+ // Fetch the latest release.
+ apiURL = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", g.Owner, g.Repo)
+ } else {
+ // Fetch a specific version.
+ apiURL = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", g.Owner, g.Repo, version)
+ }
+
+ resp, err := http.Get(apiURL)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var releaseData ReleaseData
+ err = json.Unmarshal(body, &releaseData)
+ if err != nil {
+ return nil, err
+ }
+
+ return &releaseData, nil
+}
+
+func main() {
+ repo := &GitHubRepo{Owner: repoOwner, Repo: repoName}
+
+ // Get the version from command line arguments, if provided
+ var version string // Default is use latest
+
+ if len(os.Args) > 1 {
+ version = os.Args[1] // Use the provided version
+ }
+
+ // Fetch release data (either for latest or specific version)
+ releaseData, err := repo.fetchReleaseData(version)
+ if err != nil {
+ fmt.Println("Error fetching release data:", err)
+ return
+ }
+
+ // Generate and print the formatted changelog
+ changelog := repo.generateChangelog(releaseData.TagName, releaseData.Published, releaseData.HtmlUrl, releaseData.Body)
+ fmt.Println(changelog)
+}
diff --git a/version/version.go b/version/version.go
index 23b3a82f5..32ad27808 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,6 +1,14 @@
package version
-import _ "embed"
+import (
+ _ "embed"
+ "strings"
+)
//go:embed version
var Version string
+
+func init() {
+ Version = strings.Trim(Version, "\n")
+ Version = strings.TrimSpace(Version)
+}
diff --git a/wasm/cmd/Makefile b/wasm/cmd/Makefile
index 6b42a341b..791b4f205 100644
--- a/wasm/cmd/Makefile
+++ b/wasm/cmd/Makefile
@@ -3,7 +3,7 @@
WASM_BIN_NAME=openIM.wasm
-#新加target的时候,下面的命令行记得用tab顶格
+# When adding a new target, remember to align the following command line with a tab at the beginning for proper indentation.
wasm:
GOOS=js GOARCH=wasm go build -trimpath -ldflags "-s -w" -o ${WASM_BIN_NAME} main.go
diff --git a/wasm/cmd/main.go b/wasm/cmd/main.go
index f61eb1deb..ab1da8809 100644
--- a/wasm/cmd/main.go
+++ b/wasm/cmd/main.go
@@ -54,10 +54,6 @@ func registerFunc() {
wrapperConMsg := wasm_wrapper.NewWrapperConMsg(globalFuc)
js.Global().Set("createTextMessage", js.FuncOf(wrapperConMsg.CreateTextMessage))
js.Global().Set("createImageMessage", js.FuncOf(wrapperConMsg.CreateImageMessage))
- js.Global().Set("createImageMessageByURL", js.FuncOf(wrapperConMsg.CreateImageMessageByURL))
- js.Global().Set("createSoundMessageByURL", js.FuncOf(wrapperConMsg.CreateSoundMessageByURL))
- js.Global().Set("createVideoMessageByURL", js.FuncOf(wrapperConMsg.CreateVideoMessageByURL))
- js.Global().Set("createFileMessageByURL", js.FuncOf(wrapperConMsg.CreateFileMessageByURL))
js.Global().Set("createCustomMessage", js.FuncOf(wrapperConMsg.CreateCustomMessage))
js.Global().Set("createQuoteMessage", js.FuncOf(wrapperConMsg.CreateQuoteMessage))
js.Global().Set("createAdvancedQuoteMessage", js.FuncOf(wrapperConMsg.CreateAdvancedQuoteMessage))
@@ -70,21 +66,15 @@ func registerFunc() {
js.Global().Set("createFaceMessage", js.FuncOf(wrapperConMsg.CreateFaceMessage))
js.Global().Set("createForwardMessage", js.FuncOf(wrapperConMsg.CreateForwardMessage))
js.Global().Set("createLocationMessage", js.FuncOf(wrapperConMsg.CreateLocationMessage))
- js.Global().Set("createVideoMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateVideoMessageFromFullPath))
- js.Global().Set("createImageMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateImageMessageFromFullPath))
- js.Global().Set("createSoundMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateSoundMessageFromFullPath))
- js.Global().Set("createFileMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateFileMessageFromFullPath))
js.Global().Set("createSoundMessage", js.FuncOf(wrapperConMsg.CreateSoundMessage))
js.Global().Set("createForwardMessage", js.FuncOf(wrapperConMsg.CreateForwardMessage))
js.Global().Set("createLocationMessage", js.FuncOf(wrapperConMsg.CreateLocationMessage))
- js.Global().Set("createVideoMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateVideoMessageFromFullPath))
- js.Global().Set("createImageMessageFromFullPath", js.FuncOf(wrapperConMsg.CreateImageMessageFromFullPath))
js.Global().Set("getAtAllTag", js.FuncOf(wrapperConMsg.GetAtAllTag))
js.Global().Set("markConversationMessageAsRead", js.FuncOf(wrapperConMsg.MarkConversationMessageAsRead))
+ js.Global().Set("markAllConversationMessageAsRead", js.FuncOf(wrapperConMsg.MarkAllConversationMessageAsRead))
js.Global().Set("markMessagesAsReadByMsgID", js.FuncOf(wrapperConMsg.MarkMessagesAsReadByMsgID))
js.Global().Set("sendMessage", js.FuncOf(wrapperConMsg.SendMessage))
- js.Global().Set("sendMessageNotOss", js.FuncOf(wrapperConMsg.SendMessageNotOss))
//js.Global().Set("setMessageReactionExtensions", js.FuncOf(wrapperConMsg.SetMessageReactionExtensions))
//js.Global().Set("addMessageReactionExtensions", js.FuncOf(wrapperConMsg.AddMessageReactionExtensions))
//js.Global().Set("deleteMessageReactionExtensions", js.FuncOf(wrapperConMsg.DeleteMessageReactionExtensions))
@@ -97,17 +87,11 @@ func registerFunc() {
js.Global().Set("getAdvancedHistoryMessageList", js.FuncOf(wrapperConMsg.GetAdvancedHistoryMessageList))
js.Global().Set("getAdvancedHistoryMessageListReverse", js.FuncOf(wrapperConMsg.GetAdvancedHistoryMessageListReverse))
js.Global().Set("getMultipleConversation", js.FuncOf(wrapperConMsg.GetMultipleConversation))
- js.Global().Set("setConversationPrivateChat", js.FuncOf(wrapperConMsg.SetConversationPrivateChat))
- js.Global().Set("setConversationRecvMessageOpt", js.FuncOf(wrapperConMsg.SetConversationRecvMessageOpt))
- js.Global().Set("setGlobalRecvMessageOpt", js.FuncOf(wrapperConMsg.SetGlobalRecvMessageOpt))
js.Global().Set("hideConversation", js.FuncOf(wrapperConMsg.HideConversation))
js.Global().Set("setConversationDraft", js.FuncOf(wrapperConMsg.SetConversationDraft))
- js.Global().Set("searchConversation", js.FuncOf(wrapperConMsg.SearchConversation))
- js.Global().Set("resetConversationGroupAtType", js.FuncOf(wrapperConMsg.ResetConversationGroupAtType))
- js.Global().Set("pinConversation", js.FuncOf(wrapperConMsg.PinConversation))
+ js.Global().Set("setConversation", js.FuncOf(wrapperConMsg.SetConversation))
+
js.Global().Set("getTotalUnreadMsgCount", js.FuncOf(wrapperConMsg.GetTotalUnreadMsgCount))
- js.Global().Set("setConversationBurnDuration", js.FuncOf(wrapperConMsg.SetConversationBurnDuration))
- js.Global().Set("setConversationEx", js.FuncOf(wrapperConMsg.SetConversationEx))
js.Global().Set("findMessageList", js.FuncOf(wrapperConMsg.FindMessageList))
js.Global().Set("revokeMessage", js.FuncOf(wrapperConMsg.RevokeMessage))
@@ -122,11 +106,12 @@ func registerFunc() {
js.Global().Set("insertGroupMessageToLocalStorage", js.FuncOf(wrapperConMsg.InsertGroupMessageToLocalStorage))
js.Global().Set("searchLocalMessages", js.FuncOf(wrapperConMsg.SearchLocalMessages))
js.Global().Set("setMessageLocalEx", js.FuncOf(wrapperConMsg.SetMessageLocalEx))
+ js.Global().Set("searchConversation", js.FuncOf(wrapperConMsg.SearchConversation))
js.Global().Set("changeInputStates", js.FuncOf(wrapperConMsg.ChangeInputStates))
js.Global().Set("getInputStates", js.FuncOf(wrapperConMsg.GetInputStates))
- //register group funcation
+ //register group func
wrapperGroup := wasm_wrapper.NewWrapperGroup(globalFuc)
js.Global().Set("createGroup", js.FuncOf(wrapperGroup.CreateGroup))
js.Global().Set("getSpecifiedGroupsInfo", js.FuncOf(wrapperGroup.GetSpecifiedGroupsInfo))
@@ -135,15 +120,15 @@ func registerFunc() {
js.Global().Set("dismissGroup", js.FuncOf(wrapperGroup.DismissGroup))
js.Global().Set("changeGroupMute", js.FuncOf(wrapperGroup.ChangeGroupMute))
js.Global().Set("changeGroupMemberMute", js.FuncOf(wrapperGroup.ChangeGroupMemberMute))
- js.Global().Set("setGroupMemberRoleLevel", js.FuncOf(wrapperGroup.SetGroupMemberRoleLevel))
+ //js.Global().Set("setGroupMemberRoleLevel", js.FuncOf(wrapperGroup.SetGroupMemberRoleLevel))
js.Global().Set("setGroupMemberInfo", js.FuncOf(wrapperGroup.SetGroupMemberInfo))
js.Global().Set("getJoinedGroupList", js.FuncOf(wrapperGroup.GetJoinedGroupList))
js.Global().Set("getJoinedGroupListPage", js.FuncOf(wrapperGroup.GetJoinedGroupListPage))
js.Global().Set("searchGroups", js.FuncOf(wrapperGroup.SearchGroups))
js.Global().Set("setGroupInfo", js.FuncOf(wrapperGroup.SetGroupInfo))
- js.Global().Set("setGroupVerification", js.FuncOf(wrapperGroup.SetGroupVerification))
- js.Global().Set("setGroupLookMemberInfo", js.FuncOf(wrapperGroup.SetGroupLookMemberInfo))
- js.Global().Set("setGroupApplyMemberFriend", js.FuncOf(wrapperGroup.SetGroupApplyMemberFriend))
+ //js.Global().Set("setGroupVerification", js.FuncOf(wrapperGroup.SetGroupVerification))
+ //js.Global().Set("setGroupLookMemberInfo", js.FuncOf(wrapperGroup.SetGroupLookMemberInfo))
+ //js.Global().Set("setGroupApplyMemberFriend", js.FuncOf(wrapperGroup.SetGroupApplyMemberFriend))
js.Global().Set("getGroupMemberList", js.FuncOf(wrapperGroup.GetGroupMemberList))
js.Global().Set("getGroupMemberOwnerAndAdmin", js.FuncOf(wrapperGroup.GetGroupMemberOwnerAndAdmin))
js.Global().Set("getGroupMemberListByJoinTimeFilter", js.FuncOf(wrapperGroup.GetGroupMemberListByJoinTimeFilter))
@@ -155,7 +140,7 @@ func registerFunc() {
js.Global().Set("getGroupApplicationListAsApplicant", js.FuncOf(wrapperGroup.GetGroupApplicationListAsApplicant))
js.Global().Set("acceptGroupApplication", js.FuncOf(wrapperGroup.AcceptGroupApplication))
js.Global().Set("refuseGroupApplication", js.FuncOf(wrapperGroup.RefuseGroupApplication))
- js.Global().Set("setGroupMemberNickname", js.FuncOf(wrapperGroup.SetGroupMemberNickname))
+ //js.Global().Set("setGroupMemberNickname", js.FuncOf(wrapperGroup.SetGroupMemberNickname))
js.Global().Set("searchGroupMembers", js.FuncOf(wrapperGroup.SearchGroupMembers))
js.Global().Set("isJoinGroup", js.FuncOf(wrapperGroup.IsJoinGroup))
js.Global().Set("getUsersInGroup", js.FuncOf(wrapperGroup.GetUsersInGroup))
@@ -163,9 +148,8 @@ func registerFunc() {
wrapperUser := wasm_wrapper.NewWrapperUser(globalFuc)
js.Global().Set("getSelfUserInfo", js.FuncOf(wrapperUser.GetSelfUserInfo))
js.Global().Set("setSelfInfo", js.FuncOf(wrapperUser.SetSelfInfo))
- js.Global().Set("setSelfInfoEx", js.FuncOf(wrapperUser.SetSelfInfoEx))
+ //js.Global().Set("setSelfInfoEx", js.FuncOf(wrapperUser.SetSelfInfo))
js.Global().Set("getUsersInfo", js.FuncOf(wrapperUser.GetUsersInfo))
- js.Global().Set("getUsersInfoWithCache", js.FuncOf(wrapperUser.GetUsersInfoWithCache))
js.Global().Set("subscribeUsersStatus", js.FuncOf(wrapperUser.SubscribeUsersStatus))
js.Global().Set("unsubscribeUsersStatus", js.FuncOf(wrapperUser.UnsubscribeUsersStatus))
js.Global().Set("getSubscribeUsersStatus", js.FuncOf(wrapperUser.GetSubscribeUsersStatus))
@@ -181,8 +165,9 @@ func registerFunc() {
js.Global().Set("searchFriends", js.FuncOf(wrapperFriend.SearchFriends))
js.Global().Set("checkFriend", js.FuncOf(wrapperFriend.CheckFriend))
js.Global().Set("addFriend", js.FuncOf(wrapperFriend.AddFriend))
- js.Global().Set("setFriendRemark", js.FuncOf(wrapperFriend.SetFriendRemark))
- js.Global().Set("pinFriends", js.FuncOf(wrapperFriend.PinFriends))
+ //js.Global().Set("setFriendRemark", js.FuncOf(wrapperFriend.SetFriendRemark))
+ //js.Global().Set("pinFriends", js.FuncOf(wrapperFriend.PinFriends))
+ js.Global().Set("updateFriends", js.FuncOf(wrapperFriend.UpdateFriends))
js.Global().Set("deleteFriend", js.FuncOf(wrapperFriend.DeleteFriend))
js.Global().Set("getFriendApplicationListAsRecipient", js.FuncOf(wrapperFriend.GetFriendApplicationListAsRecipient))
js.Global().Set("getFriendApplicationListAsApplicant", js.FuncOf(wrapperFriend.GetFriendApplicationListAsApplicant))
@@ -191,7 +176,7 @@ func registerFunc() {
js.Global().Set("getBlackList", js.FuncOf(wrapperFriend.GetBlackList))
js.Global().Set("removeBlack", js.FuncOf(wrapperFriend.RemoveBlack))
js.Global().Set("addBlack", js.FuncOf(wrapperFriend.AddBlack))
- js.Global().Set("setFriendsEx", js.FuncOf(wrapperFriend.SetFriendsEx))
+ //js.Global().Set("setFriendsEx", js.FuncOf(wrapperFriend.SetFriendsEx))
wrapperThird := wasm_wrapper.NewWrapperThird(globalFuc)
js.Global().Set("updateFcmToken", js.FuncOf(wrapperThird.UpdateFcmToken))
diff --git a/wasm/cmd/openIM.wasm b/wasm/cmd/openIM.wasm
deleted file mode 100755
index 7f43ff84c..000000000
Binary files a/wasm/cmd/openIM.wasm and /dev/null differ
diff --git a/wasm/cmd/static/wasm_exec.js b/wasm/cmd/static/wasm_exec.js
index 9cd1ba7ea..4bb58a9d9 100644
--- a/wasm/cmd/static/wasm_exec.js
+++ b/wasm/cmd/static/wasm_exec.js
@@ -1,7 +1,3 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
"use strict";
(() => {
@@ -212,7 +208,7 @@
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
// This changes the SP, thus we have to update the SP used by the imported function.
- // funcation wasmExit(code int32)
+ // func wasmExit(code int32)
"runtime.wasmExit": (sp) => {
sp >>>= 0;
const code = this.mem.getInt32(sp + 8, true);
@@ -225,7 +221,7 @@
this.exit(code);
},
- // funcation wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
"runtime.wasmWrite": (sp) => {
sp >>>= 0;
const fd = getInt64(sp + 8);
@@ -234,19 +230,19 @@
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
},
- // funcation resetMemoryDataView()
+ // func resetMemoryDataView()
"runtime.resetMemoryDataView": (sp) => {
sp >>>= 0;
this.mem = new DataView(this._inst.exports.mem.buffer);
},
- // funcation nanotime1() int64
+ // func nanotime1() int64
"runtime.nanotime1": (sp) => {
sp >>>= 0;
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
- // funcation walltime() (sec int64, nsec int32)
+ // func walltime() (sec int64, nsec int32)
"runtime.walltime": (sp) => {
sp >>>= 0;
const msec = (new Date).getTime();
@@ -254,7 +250,7 @@
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
- // funcation scheduleTimeoutEvent(delay int64) int32
+ // func scheduleTimeoutEvent(delay int64) int32
"runtime.scheduleTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this._nextCallbackTimeoutID;
@@ -274,7 +270,7 @@
this.mem.setInt32(sp + 16, id, true);
},
- // funcation clearTimeoutEvent(id int32)
+ // func clearTimeoutEvent(id int32)
"runtime.clearTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this.mem.getInt32(sp + 8, true);
@@ -282,13 +278,13 @@
this._scheduledTimeouts.delete(id);
},
- // funcation getRandomData(r []byte)
+ // func getRandomData(r []byte)
"runtime.getRandomData": (sp) => {
sp >>>= 0;
crypto.getRandomValues(loadSlice(sp + 8));
},
- // funcation finalizeRef(v ref)
+ // func finalizeRef(v ref)
"syscall/js.finalizeRef": (sp) => {
sp >>>= 0;
const id = this.mem.getUint32(sp + 8, true);
@@ -301,13 +297,13 @@
}
},
- // funcation stringVal(value string) ref
+ // func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
sp >>>= 0;
storeValue(sp + 24, loadString(sp + 8));
},
- // funcation valueGet(v ref, p string) ref
+ // func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
sp >>>= 0;
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
@@ -315,19 +311,19 @@
storeValue(sp + 32, result);
},
- // funcation valueSet(v ref, p string, x ref)
+ // func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
sp >>>= 0;
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
- // funcation valueDelete(v ref, p string)
+ // func valueDelete(v ref, p string)
"syscall/js.valueDelete": (sp) => {
sp >>>= 0;
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
},
- // funcation valueIndex(v ref, i int) ref
+ // func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
sp >>>= 0;
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
@@ -339,7 +335,7 @@
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
- // funcation valueCall(v ref, m string, args []ref) (ref, bool)
+ // func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
sp >>>= 0;
try {
@@ -357,7 +353,7 @@
}
},
- // funcation valueInvoke(v ref, args []ref) (ref, bool)
+ // func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
sp >>>= 0;
try {
@@ -374,7 +370,7 @@
}
},
- // funcation valueNew(v ref, args []ref) (ref, bool)
+ // func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
sp >>>= 0;
try {
@@ -391,7 +387,7 @@
}
},
- // funcation valueLength(v ref) int
+ // func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
sp >>>= 0;
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
@@ -412,13 +408,13 @@
loadSlice(sp + 16).set(str);
},
- // funcation valueInstanceOf(v ref, t ref) bool
+ // func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (sp) => {
sp >>>= 0;
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
},
- // funcation copyBytesToGo(dst []byte, src ref) (int, bool)
+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (sp) => {
sp >>>= 0;
const dst = loadSlice(sp + 8);
@@ -433,7 +429,7 @@
this.mem.setUint8(sp + 48, 1);
},
- // funcation copyBytesToJS(dst ref, src []byte) (int, bool)
+ // func copyBytesToJS(dst ref, src []byte) (int, bool)
"syscall/js.copyBytesToJS": (sp) => {
sp >>>= 0;
const dst = loadValue(sp + 8);
diff --git a/wasm/event_listener/caller.go b/wasm/event_listener/caller.go
index efe71ea50..0f6007d2a 100644
--- a/wasm/event_listener/caller.go
+++ b/wasm/event_listener/caller.go
@@ -21,13 +21,16 @@ import (
"bytes"
"context"
"errors"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
- "github.com/openimsdk/tools/log"
"reflect"
"strconv"
"strings"
"syscall/js"
+
+ "github.com/openimsdk/tools/errs"
+
+ "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
+ "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
+ "github.com/openimsdk/tools/log"
)
type Caller interface {
@@ -42,7 +45,7 @@ type Caller interface {
type FuncLogic func()
var ErrNotSetCallback = errors.New("not set callback to call")
-var ErrNotSetFunc = errors.New("not set funcation to call")
+var ErrNotSetFunc = errors.New("not set func to call")
type ReflectCall struct {
funcName interface{}
@@ -149,7 +152,8 @@ func (r *ReflectCall) asyncCallWithOutCallback() {
if r.callback == nil {
r.callback = NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), nil)
}
- log.ZError(ctx, "test", nil, "asyncCallWithOutCallback", len(r.arguments))
+ log.ZWarn(ctx, "asyncCall", nil, "asyncCallWithOutCallback", len(r.arguments))
+
r.callback.SetOperationID(r.arguments[0].String())
//strings.SplitAfter()
for i := 0; i < len(r.arguments); i++ {
@@ -275,7 +279,7 @@ func (r *ReflectCall) ErrHandle(recover interface{}) []string {
switch x := recover.(type) {
case string:
log.ZError(ctx, "STRINGERR", nil, "r", x)
- temp = utils.Wrap(errors.New(x), "").Error()
+ temp = errs.WrapMsg(errors.New(x), "").Error()
case error:
//buf := make([]byte, 1<<20)
//runtime.Stack(buf, true)
@@ -283,7 +287,7 @@ func (r *ReflectCall) ErrHandle(recover interface{}) []string {
temp = x.Error()
default:
log.ZError(ctx, "unknown panic", nil, "r", x)
- temp = utils.Wrap(errors.New("unknown panic"), "").Error()
+ temp = errs.WrapMsg(errors.New("unknown panic"), "").Error()
}
if r.callback != nil {
r.callback.SetErrCode(100).SetErrMsg(temp).SendMessage()
diff --git a/wasm/event_listener/listener.go b/wasm/event_listener/listener.go
index 8f892ec84..1d5aba5b4 100644
--- a/wasm/event_listener/listener.go
+++ b/wasm/event_listener/listener.go
@@ -20,7 +20,7 @@ package event_listener
import (
"syscall/js"
- "github.com/openimsdk/openim-sdk-core/v3/internal/file"
+ "github.com/openimsdk/openim-sdk-core/v3/internal/third/file"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
@@ -170,6 +170,10 @@ func (a AdvancedMsgCallback) OnRecvOnlineOnlyMessage(message string) {
a.CallbackWriter.SetEvent(utils.GetSelfFuncName()).SetData(message).SendMessage()
}
+func (a AdvancedMsgCallback) OnMsgEdited(message string) {
+ a.CallbackWriter.SetEvent(utils.GetSelfFuncName()).SetData(message).SendMessage()
+}
+
type BaseCallback struct {
CallbackWriter
}
@@ -308,13 +312,6 @@ func NewBatchMessageCallback(callback *js.Value) *BatchMessageCallback {
return &BatchMessageCallback{CallbackWriter: NewEventData(callback)}
}
-func (b *BatchMessageCallback) OnRecvNewMessages(messageList string) {
- b.CallbackWriter.SetEvent(utils.GetSelfFuncName()).SetData(messageList).SendMessage()
-}
-func (b *BatchMessageCallback) OnRecvOfflineNewMessages(messageList string) {
- b.CallbackWriter.SetEvent(utils.GetSelfFuncName()).SetData(messageList).SendMessage()
-}
-
type FriendCallback struct {
CallbackWriter
}
diff --git a/wasm/exec/executor.go b/wasm/exec/executor.go
index 5c5914617..635d1399f 100644
--- a/wasm/exec/executor.go
+++ b/wasm/exec/executor.go
@@ -50,11 +50,11 @@ func Exec(args ...interface{}) (output interface{}, err error) {
if r := recover(); r != nil {
switch x := r.(type) {
case string:
- err = utils.Wrap(errors.New(x), "")
+ err = errs.WrapMsg(errors.New(x), "")
case error:
err = x
default:
- err = utils.Wrap(errors.New("unknown panic"), "")
+ err = errs.WrapMsg(errors.New("unknown panic"), "")
}
}
}()
@@ -70,11 +70,11 @@ func Exec(args ...interface{}) (output interface{}, err error) {
if r := recover(); r != nil {
switch x := r.(type) {
case string:
- err = utils.Wrap(errors.New(x), "")
+ err = errs.WrapMsg(errors.New(x), "")
case error:
err = x
default:
- err = utils.Wrap(errors.New("unknown panic"), "")
+ err = errs.WrapMsg(errors.New("unknown panic"), "")
}
}
}()
@@ -89,11 +89,11 @@ func Exec(args ...interface{}) (output interface{}, err error) {
if r := recover(); r != nil {
switch x := r.(type) {
case string:
- err = utils.Wrap(errors.New(x), "")
+ err = errs.WrapMsg(errors.New(x), "")
case error:
err = x
default:
- err = utils.Wrap(errors.New("unknown panic"), "")
+ err = errs.WrapMsg(errors.New("unknown panic"), "")
}
}
}()
@@ -110,7 +110,7 @@ func Exec(args ...interface{}) (output interface{}, err error) {
case js.TypeString:
interErr := utils.JsonStringToStruct(result[0].String(), &data)
if interErr != nil {
- err = utils.Wrap(err, "return json unmarshal err from javascript")
+ err = errs.WrapMsg(err, "return json unmarshal err from javascript")
}
case js.TypeObject:
return result[0], nil
diff --git a/wasm/indexdb/cache_message.go b/wasm/indexdb/cache_message.go
deleted file mode 100644
index 223a6fe00..000000000
--- a/wasm/indexdb/cache_message.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build js && wasm
-// +build js,wasm
-
-package indexdb
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
-)
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
-)
-
-type LocalCacheMessage struct {
-}
-
-func NewLocalCacheMessage() *LocalCacheMessage {
- return &LocalCacheMessage{}
-}
-
-func (i *LocalCacheMessage) BatchInsertTempCacheMessageList(ctx context.Context, MessageList []*model_struct.TempCacheLocalChatLog) error {
- _, err := exec.Exec(utils.StructToJsonString(MessageList))
- return err
-}
-
-func (i *LocalCacheMessage) InsertTempCacheMessage(ctx context.Context, Message *model_struct.TempCacheLocalChatLog) error {
- _, err := exec.Exec(utils.StructToJsonString(Message))
- return err
-}
diff --git a/wasm/indexdb/chat_log_model.go b/wasm/indexdb/chat_log_model.go
index 5c9c208b3..d594e8d48 100644
--- a/wasm/indexdb/chat_log_model.go
+++ b/wasm/indexdb/chat_log_model.go
@@ -24,6 +24,7 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
"github.com/openimsdk/openim-sdk-core/v3/wasm/indexdb/temp_struct"
+ "github.com/openimsdk/tools/errs"
)
type LocalChatLogs struct {
@@ -60,26 +61,23 @@ func (i *LocalChatLogs) UpdateMessage(ctx context.Context, conversationID string
return exec.PrimaryKeyNull
}
tempLocalChatLog := temp_struct.LocalChatLog{
- ServerMsgID: c.ServerMsgID,
- SendID: c.SendID,
- RecvID: c.RecvID,
- SenderPlatformID: c.SenderPlatformID,
- SenderNickname: c.SenderNickname,
- SenderFaceURL: c.SenderFaceURL,
- SessionType: c.SessionType,
- MsgFrom: c.MsgFrom,
- ContentType: c.ContentType,
- Content: c.Content,
- IsRead: c.IsRead,
- Status: c.Status,
- Seq: c.Seq,
- SendTime: c.SendTime,
- CreateTime: c.CreateTime,
- AttachedInfo: c.AttachedInfo,
- Ex: c.Ex,
- IsReact: c.IsReact,
- IsExternalExtensions: c.IsExternalExtensions,
- MsgFirstModifyTime: c.MsgFirstModifyTime,
+ ServerMsgID: c.ServerMsgID,
+ SendID: c.SendID,
+ RecvID: c.RecvID,
+ SenderPlatformID: c.SenderPlatformID,
+ SenderNickname: c.SenderNickname,
+ SenderFaceURL: c.SenderFaceURL,
+ SessionType: c.SessionType,
+ MsgFrom: c.MsgFrom,
+ ContentType: c.ContentType,
+ Content: c.Content,
+ IsRead: c.IsRead,
+ Status: c.Status,
+ Seq: c.Seq,
+ SendTime: c.SendTime,
+ CreateTime: c.CreateTime,
+ AttachedInfo: c.AttachedInfo,
+ Ex: c.Ex,
}
_, err := exec.Exec(conversationID, c.ClientMsgID, utils.StructToJsonString(tempLocalChatLog))
return err
@@ -128,8 +126,8 @@ func (i *LocalChatLogs) UpdateMessageTimeAndStatus(ctx context.Context, conversa
}
// GetMessageList retrieves a list of messages from the local chat log.
-func (i *LocalChatLogs) GetMessageList(ctx context.Context, conversationID string, count int, startTime int64, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- msgList, err := exec.Exec(conversationID, count, startTime, isReverse, i.loginUserID)
+func (i *LocalChatLogs) GetMessageList(ctx context.Context, conversationID string, count int, startTime, startSeq int64, startClientMsgID string, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
+ msgList, err := exec.Exec(conversationID, count, startTime, startSeq, startClientMsgID, isReverse, i.loginUserID)
if err != nil {
return nil, err
} else {
@@ -150,22 +148,17 @@ func (i *LocalChatLogs) GetMessageList(ctx context.Context, conversationID strin
}
}
-// GetMessageListNoTime retrieves a list of messages from the local chat log without specifying a start time.
-func (i *LocalChatLogs) GetMessageListNoTime(ctx context.Context, conversationID string, count int, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
- msgList, err := exec.Exec(conversationID, count, isReverse)
+func (i *LocalChatLogs) GetLatestActiveMessage(ctx context.Context, conversationID string, isReverse bool) (result []*model_struct.LocalChatLog, err error) {
+ msg, err := exec.Exec(conversationID, isReverse)
if err != nil {
return nil, err
} else {
- if v, ok := msgList.(string); ok {
- var temp []model_struct.LocalChatLog
- err := utils.JsonStringToStruct(v, &temp)
+ if v, ok := msg.(string); ok {
+ err := utils.JsonStringToStruct(v, &result)
if err != nil {
return nil, err
}
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
+
return result, err
} else {
return nil, exec.ErrType
@@ -173,6 +166,27 @@ func (i *LocalChatLogs) GetMessageListNoTime(ctx context.Context, conversationID
}
}
+func (i *LocalChatLogs) GetLatestValidServerMessage(ctx context.Context, conversationID string, startTime int64, isReverse bool) (*model_struct.LocalChatLog, error) {
+ msg, err := exec.Exec(conversationID, startTime, isReverse)
+ if err != nil {
+ if errs.ErrRecordNotFound.Is(err) {
+ return nil, nil
+ }
+ return nil, err
+ } else {
+ if v, ok := msg.(string); ok {
+ result := model_struct.LocalChatLog{}
+ err := utils.JsonStringToStruct(v, &result)
+ if err != nil {
+ return nil, err
+ }
+ return &result, err
+ } else {
+ return nil, exec.ErrType
+ }
+ }
+}
+
// UpdateSingleMessageHasRead updates the hasRead field of a single message in the local chat log.
func (i *LocalChatLogs) UpdateSingleMessageHasRead(ctx context.Context, sendID string, msgIDList []string) error {
_, err := exec.Exec(sendID, utils.StructToJsonString(msgIDList))
@@ -180,8 +194,8 @@ func (i *LocalChatLogs) UpdateSingleMessageHasRead(ctx context.Context, sendID s
}
// SearchMessageByContentType searches for messages in the local chat log by content type.
-func (i *LocalChatLogs) SearchMessageByContentType(ctx context.Context, contentType []int, conversationID string, startTime, endTime int64, offset, count int) (messages []*model_struct.LocalChatLog, err error) {
- msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), startTime, endTime, offset, count)
+func (i *LocalChatLogs) SearchMessageByContentType(ctx context.Context, contentType []int, senderUserIDList []string, conversationID string, startTime, endTime int64, offset, count int) (messages []*model_struct.LocalChatLog, err error) {
+ msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), utils.StructToJsonString(senderUserIDList), startTime, endTime, offset, count)
if err != nil {
return nil, err
} else {
@@ -202,44 +216,19 @@ func (i *LocalChatLogs) SearchMessageByContentType(ctx context.Context, contentT
}
}
-//funcation (i *LocalChatLogs) SuperGroupSearchMessageByContentType(ctx context.Context, contentType []int, sourceID string, startTime, endTime int64, sessionType, offset, count int) (messages []*model_struct.LocalChatLog, err error) {
-// msgList, err := Exec(utils.StructToJsonString(contentType), sourceID, startTime, endTime, sessionType, offset, count)
-// if err != nil {
-// return nil, err
-// } else {
-// if v, ok := msgList.(string); ok {
-// var temp []model_struct.LocalChatLog
-// err := utils.JsonStringToStruct(v, &temp)
-// if err != nil {
-// return nil, err
-// }
-// for _, v := range temp {
-// v1 := v
-// messages = append(messages, &v1)
-// }
-// return messages, err
-// } else {
-// return nil, ErrType
-// }
-// }
-//}
-
// SearchMessageByKeyword searches for messages in the local chat log by keyword.
-func (i *LocalChatLogs) SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
- msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), utils.StructToJsonString(keywordList), keywordListMatchType, startTime, endTime)
+func (i *LocalChatLogs) SearchMessageByContentTypeAndKeyword(ctx context.Context, contentType []int, conversationID string, senderUserIDList []string, keywordList []string, keywordListMatchType int, startTime, endTime int64) (result []*model_struct.LocalChatLog, err error) {
+ msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), utils.StructToJsonString(senderUserIDList), utils.StructToJsonString(keywordList), keywordListMatchType, startTime, endTime)
if err != nil {
return nil, err
} else {
if v, ok := msgList.(string); ok {
- var temp []model_struct.LocalChatLog
- err := utils.JsonStringToStruct(v, &temp)
+ var result []*model_struct.LocalChatLog
+ err := utils.JsonStringToStruct(v, &result)
if err != nil {
return nil, err
}
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
+
return result, err
} else {
return nil, exec.ErrType
@@ -344,18 +333,6 @@ func (i *LocalChatLogs) GetTestMessage(ctx context.Context, seq uint32) (*model_
}
}
-// Update the sender's nickname in the chat logs
-func (i *LocalChatLogs) UpdateMsgSenderNickname(ctx context.Context, sendID, nickname string, sType int) error {
- _, err := exec.Exec(sendID, nickname, sType)
- return err
-}
-
-// Update the sender's face URL in the chat logs
-func (i *LocalChatLogs) UpdateMsgSenderFaceURL(ctx context.Context, sendID, faceURL string, sType int) error {
- _, err := exec.Exec(sendID, faceURL, sType)
- return err
-}
-
// Update the sender's face URL and nickname in the chat logs
func (i *LocalChatLogs) UpdateMsgSenderFaceURLAndSenderNickname(ctx context.Context, conversationID, sendID, faceURL, nickname string) error {
_, err := exec.Exec(conversationID, sendID, faceURL, nickname)
@@ -451,42 +428,6 @@ func (i *LocalChatLogs) GetMsgSeqListBySelfUserID(ctx context.Context, userID st
}
}
-// Get the abnormal message sequence number
-func (i *LocalChatLogs) GetAbnormalMsgSeq(ctx context.Context) (int64, error) {
- result, err := exec.Exec()
- if err != nil {
- return 0, err
- }
- if v, ok := result.(float64); ok {
- return int64(v), nil
- }
- return 0, exec.ErrType
-}
-
-// Get the list of abnormal message sequence numbers
-func (i *LocalChatLogs) GetAbnormalMsgSeqList(ctx context.Context) (result []int64, err error) {
- l, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := l.(string); ok {
- err := utils.JsonStringToStruct(v, &result)
- if err != nil {
- return nil, err
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-// Batch insert exception messages into the chat logs
-func (i *LocalChatLogs) BatchInsertExceptionMsg(ctx context.Context, MessageList []*model_struct.LocalErrChatLog) error {
- _, err := exec.Exec(utils.StructToJsonString(MessageList))
- return err
-}
-
// Update the message status to read in the chat logs
func (i *LocalChatLogs) UpdateGroupMessageHasRead(ctx context.Context, msgIDList []string, sessionType int32) error {
_, err := exec.Exec(utils.StructToJsonString(msgIDList), sessionType)
@@ -494,21 +435,18 @@ func (i *LocalChatLogs) UpdateGroupMessageHasRead(ctx context.Context, msgIDList
}
// Get the message by message ID
-func (i *LocalChatLogs) SearchMessageByKeyword(ctx context.Context, contentType []int, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
- msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), utils.StructToJsonString(keywordList), keywordListMatchType, startTime, endTime, offset, count)
+func (i *LocalChatLogs) SearchMessageByKeyword(ctx context.Context, contentType []int, senderUserIDList []string, keywordList []string, keywordListMatchType int, conversationID string, startTime, endTime int64, offset, count int) (result []*model_struct.LocalChatLog, err error) {
+ msgList, err := exec.Exec(conversationID, utils.StructToJsonString(contentType), utils.StructToJsonString(senderUserIDList), utils.StructToJsonString(keywordList), keywordListMatchType, startTime, endTime, offset, count)
if err != nil {
return nil, err
} else {
if v, ok := msgList.(string); ok {
- var temp []model_struct.LocalChatLog
- err := utils.JsonStringToStruct(v, &temp)
+ var result []*model_struct.LocalChatLog
+ err := utils.JsonStringToStruct(v, &result)
if err != nil {
return nil, err
}
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
+
return result, err
} else {
return nil, exec.ErrType
@@ -573,26 +511,23 @@ func (i *LocalChatLogs) UpdateMessageBySeq(ctx context.Context, conversationID s
return exec.PrimaryKeyNull
}
tempLocalChatLog := temp_struct.LocalChatLog{
- ServerMsgID: c.ServerMsgID,
- SendID: c.SendID,
- RecvID: c.RecvID,
- SenderPlatformID: c.SenderPlatformID,
- SenderNickname: c.SenderNickname,
- SenderFaceURL: c.SenderFaceURL,
- SessionType: c.SessionType,
- MsgFrom: c.MsgFrom,
- ContentType: c.ContentType,
- Content: c.Content,
- IsRead: c.IsRead,
- Status: c.Status,
- Seq: c.Seq,
- SendTime: c.SendTime,
- CreateTime: c.CreateTime,
- AttachedInfo: c.AttachedInfo,
- Ex: c.Ex,
- IsReact: c.IsReact,
- IsExternalExtensions: c.IsExternalExtensions,
- MsgFirstModifyTime: c.MsgFirstModifyTime,
+ ServerMsgID: c.ServerMsgID,
+ SendID: c.SendID,
+ RecvID: c.RecvID,
+ SenderPlatformID: c.SenderPlatformID,
+ SenderNickname: c.SenderNickname,
+ SenderFaceURL: c.SenderFaceURL,
+ SessionType: c.SessionType,
+ MsgFrom: c.MsgFrom,
+ ContentType: c.ContentType,
+ Content: c.Content,
+ IsRead: c.IsRead,
+ Status: c.Status,
+ Seq: c.Seq,
+ SendTime: c.SendTime,
+ CreateTime: c.CreateTime,
+ AttachedInfo: c.AttachedInfo,
+ Ex: c.Ex,
}
_, err := exec.Exec(conversationID, c.Seq, utils.StructToJsonString(tempLocalChatLog))
return err
@@ -681,15 +616,12 @@ func (i *LocalChatLogs) GetMessagesBySeqs(ctx context.Context, conversationID st
return nil, err
} else {
if v, ok := msgs.(string); ok {
- var temp []model_struct.LocalChatLog
- err := utils.JsonStringToStruct(v, &temp)
+ var result []*model_struct.LocalChatLog
+ err := utils.JsonStringToStruct(v, &result)
if err != nil {
return nil, err
}
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
+
return result, err
} else {
return nil, exec.ErrType
@@ -713,7 +645,7 @@ func (i *LocalChatLogs) GetConversationNormalMsgSeq(ctx context.Context, convers
}
}
-func (i *LocalChatLogs) GetConversationNormalMsgSeqNoInit(ctx context.Context, conversationID string) (int64, error) {
+func (i *LocalChatLogs) CheckConversationNormalMsgSeq(ctx context.Context, conversationID string) (int64, error) {
seq, err := exec.Exec(conversationID)
if err != nil {
return 0, err
diff --git a/wasm/indexdb/chat_log_reaction_extension_model.go b/wasm/indexdb/chat_log_reaction_extension_model.go
deleted file mode 100644
index d1176409c..000000000
--- a/wasm/indexdb/chat_log_reaction_extension_model.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build js && wasm
-// +build js,wasm
-
-package indexdb
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
-)
-
-type LocalChatLogReactionExtensions struct {
- ExtKey string `json:"ext_key"`
- ExtVal string `json:"ext_val"`
- ExtKey2 string `json:"ext_key2"`
- ExtVal2 string `json:"ext_val2"`
-}
-
-func NewLocalChatLogReactionExtensions() *LocalChatLogReactionExtensions {
- return &LocalChatLogReactionExtensions{}
-}
-
-func (i *LocalChatLogReactionExtensions) GetMessageReactionExtension(ctx context.Context, clientMsgID string) (result *model_struct.LocalChatLogReactionExtensions, err error) {
- msg, err := exec.Exec(clientMsgID)
- if err != nil {
- return nil, err
- } else {
- if v, ok := msg.(string); ok {
- result := model_struct.LocalChatLogReactionExtensions{}
- err := utils.JsonStringToStruct(v, &result)
- if err != nil {
- return nil, err
- }
- return &result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-func (i *LocalChatLogReactionExtensions) InsertMessageReactionExtension(ctx context.Context, messageReactionExtension *model_struct.LocalChatLogReactionExtensions) error {
- _, err := exec.Exec(utils.StructToJsonString(messageReactionExtension))
- return err
-}
-
-// func (i *LocalChatLogReactionExtensions) GetAndUpdateMessageReactionExtension(ctx context.Context, clientMsgID string, m map[string]*sdkws.KeyValue) error {
-// _, err := exec.Exec(clientMsgID, utils.StructToJsonString(m))
-// return err
-// }
-//
-// func (i *LocalChatLogReactionExtensions) DeleteAndUpdateMessageReactionExtension(ctx context.Context, clientMsgID string, m map[string]*sdkws.KeyValue) error {
-// _, err := exec.Exec(clientMsgID, utils.StructToJsonString(m))
-// return err
-// }
-func (i *LocalChatLogReactionExtensions) GetMultipleMessageReactionExtension(ctx context.Context, msgIDList []string) (result []*model_struct.LocalChatLogReactionExtensions, err error) {
- msgReactionExtensionList, err := exec.Exec(utils.StructToJsonString(msgIDList))
- if err != nil {
- return nil, err
- } else {
- if v, ok := msgReactionExtensionList.(string); ok {
- var temp []model_struct.LocalChatLogReactionExtensions
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-func (i *LocalChatLogReactionExtensions) DeleteMessageReactionExtension(ctx context.Context, msgID string) error {
- _, err := exec.Exec(msgID)
- return err
-}
-func (i *LocalChatLogReactionExtensions) UpdateMessageReactionExtension(ctx context.Context, c *model_struct.LocalChatLogReactionExtensions) error {
- _, err := exec.Exec(c.ClientMsgID, utils.StructToJsonString(c))
- return err
-}
diff --git a/wasm/indexdb/conversation_model.go b/wasm/indexdb/conversation_model.go
index e0b7ae46a..e5a817fd7 100644
--- a/wasm/indexdb/conversation_model.go
+++ b/wasm/indexdb/conversation_model.go
@@ -24,6 +24,7 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
"github.com/openimsdk/openim-sdk-core/v3/wasm/indexdb/temp_struct"
+ "github.com/openimsdk/tools/errs"
)
type LocalConversations struct {
@@ -55,6 +56,23 @@ func (i *LocalConversations) GetAllConversationListDB(ctx context.Context) (resu
}
}
+func (i *LocalConversations) FindAllUnreadConversationConversationID(ctx context.Context) (result []string, err error) {
+ cList, err := exec.Exec()
+ if err != nil {
+ return nil, err
+ } else {
+ if v, ok := cList.(string); ok {
+ err := utils.JsonStringToStruct(v, &result)
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+ } else {
+ return nil, exec.ErrType
+ }
+ }
+}
+
func (i *LocalConversations) GetConversation(ctx context.Context, conversationID string) (*model_struct.LocalConversation, error) {
c, err := exec.Exec(conversationID)
if err != nil {
@@ -231,7 +249,7 @@ func (i *LocalConversations) BatchUpdateConversationList(ctx context.Context, co
for _, v := range conversationList {
err := i.UpdateConversation(ctx, v)
if err != nil {
- return utils.Wrap(err, "BatchUpdateConversationList failed")
+ return errs.WrapMsg(err, "BatchUpdateConversationList failed")
}
}
@@ -381,14 +399,14 @@ func (i *LocalConversations) SearchConversations(ctx context.Context, searchPara
// Perform the search operation. Replace the below line with the actual search logic.
searchResult, err := exec.Exec(searchParam)
if err != nil {
- return nil, utils.Wrap(err, "SearchConversations failed")
+ return nil, errs.WrapMsg(err, "SearchConversations failed")
}
// Convert searchResult to []*model_struct.LocalConversation
// Assuming searchResult is in a format that can be converted to the required type
err = utils.JsonStringToStruct(searchResult.(string), &result)
if err != nil {
- return nil, utils.Wrap(err, "Failed to parse search results")
+ return nil, errs.WrapMsg(err, "Failed to parse search results")
}
return result, nil
diff --git a/wasm/indexdb/group_member_model.go b/wasm/indexdb/group_member_model.go
index 5f37cf7ae..6edfe62a9 100644
--- a/wasm/indexdb/group_member_model.go
+++ b/wasm/indexdb/group_member_model.go
@@ -50,24 +50,6 @@ func (i *LocalGroupMember) GetGroupMemberInfoByGroupIDUserID(ctx context.Context
}
}
-func (i *LocalGroupMember) GetAllGroupMemberList(ctx context.Context) ([]model_struct.LocalGroupMember, error) {
- member, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := member.(string); ok {
- var temp []model_struct.LocalGroupMember
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return temp, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
func (i *LocalGroupMember) GetAllGroupMemberUserIDList(ctx context.Context) ([]model_struct.LocalGroupMember, error) {
member, err := exec.Exec()
if err != nil {
@@ -115,22 +97,6 @@ func (i *LocalGroupMember) GetGroupSomeMemberInfo(ctx context.Context, groupID s
}
}
-func (i *LocalGroupMember) GetGroupAdminID(ctx context.Context, groupID string) ([]string, error) {
- IDList, err := exec.Exec(groupID)
- if err != nil {
- return nil, err
- }
- if v, ok := IDList.(string); ok {
- var temp []string
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return temp, nil
- }
- return nil, exec.ErrType
-}
-
func (i *LocalGroupMember) GetGroupMemberListByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
member, err := exec.Exec(groupID)
if err != nil {
@@ -202,24 +168,6 @@ func (i *LocalGroupMember) GetGroupMemberOwnerAndAdminDB(ctx context.Context, gr
}
}
-func (i *LocalGroupMember) GetGroupMemberOwner(ctx context.Context, groupID string) (*model_struct.LocalGroupMember, error) {
- member, err := exec.Exec(groupID)
- if err != nil {
- return nil, err
- } else {
- if v, ok := member.(string); ok {
- var temp model_struct.LocalGroupMember
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return &temp, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
func (i *LocalGroupMember) GetGroupMemberListSplitByJoinTimeFilter(ctx context.Context, groupID string, offset, count int, joinTimeBegin, joinTimeEnd int64, userIDList []string) ([]*model_struct.LocalGroupMember, error) {
member, err := exec.Exec(groupID, offset, count, joinTimeBegin, joinTimeEnd, utils.StructToJsonString(userIDList))
if err != nil {
@@ -238,39 +186,6 @@ func (i *LocalGroupMember) GetGroupMemberListSplitByJoinTimeFilter(ctx context.C
}
}
-func (i *LocalGroupMember) GetGroupOwnerAndAdminByGroupID(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
- member, err := exec.Exec(groupID)
- if err != nil {
- return nil, err
- } else {
- if v, ok := member.(string); ok {
- var temp []*model_struct.LocalGroupMember
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return temp, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-func (i *LocalGroupMember) GetGroupMemberUIDListByGroupID(ctx context.Context, groupID string) (result []string, err error) {
- IDList, err := exec.Exec(groupID)
- if err != nil {
- return nil, err
- }
- if v, ok := IDList.(string); ok {
- err := utils.JsonStringToStruct(v, &result)
- if err != nil {
- return nil, err
- }
- return result, nil
- }
- return nil, exec.ErrType
-}
-
func (i *LocalGroupMember) InsertGroupMember(ctx context.Context, groupMember *model_struct.LocalGroupMember) error {
_, err := exec.Exec(utils.StructToJsonString(groupMember))
return err
@@ -296,29 +211,6 @@ func (i *LocalGroupMember) UpdateGroupMember(ctx context.Context, groupMember *m
return err
}
-func (i *LocalGroupMember) UpdateGroupMemberField(ctx context.Context, groupID, userID string, args map[string]interface{}) error {
- _, err := exec.Exec(groupID, userID, utils.StructToJsonString(args))
- return err
-}
-
-func (i *LocalGroupMember) GetGroupMemberInfoIfOwnerOrAdmin(ctx context.Context) ([]*model_struct.LocalGroupMember, error) {
- member, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := member.(string); ok {
- var temp []*model_struct.LocalGroupMember
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return temp, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
func (i *LocalGroupMember) SearchGroupMembersDB(ctx context.Context, keyword string, groupID string, isSearchMemberNickname, isSearchUserID bool, offset, count int) (result []*model_struct.LocalGroupMember, err error) {
member, err := exec.Exec(keyword, groupID, isSearchMemberNickname, isSearchUserID, offset, count)
if err != nil {
diff --git a/wasm/indexdb/group_model.go b/wasm/indexdb/group_model.go
index 7efb93125..8a169c863 100644
--- a/wasm/indexdb/group_model.go
+++ b/wasm/indexdb/group_model.go
@@ -139,29 +139,3 @@ func (i *LocalGroups) GetAllGroupInfoByGroupIDOrGroupName(ctx context.Context, k
}
}
}
-
-func (i *LocalGroups) AddMemberCount(ctx context.Context, groupID string) error {
- _, err := exec.Exec(groupID)
- return err
-}
-
-func (i *LocalGroups) SubtractMemberCount(ctx context.Context, groupID string) error {
- _, err := exec.Exec(groupID)
- return err
-}
-func (i *LocalGroups) GetGroupMemberAllGroupIDs(ctx context.Context) (result []string, err error) {
- groupIDList, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := groupIDList.(string); ok {
- err := utils.JsonStringToStruct(v, &result)
- if err != nil {
- return nil, err
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
diff --git a/wasm/indexdb/init.go b/wasm/indexdb/init.go
index 53894b3d5..4f0a73480 100644
--- a/wasm/indexdb/init.go
+++ b/wasm/indexdb/init.go
@@ -23,31 +23,34 @@ import (
"github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
)
-//1,使用wasm原生的方式,tinygo应用于go的嵌入式领域,支持的功能有限,支持go语言的子集,甚至json序列化都无法支持
-//2.函数命名遵从驼峰命名
-//3.提供的sql生成语句中,关于bool值需要特殊处理,create语句的设计的到bool值的需要在创建语句中单独说明,这是因为在原有的sqlite中并不支持bool,用整数1或者0替代,gorm对其做了转换。
-//4.提供的sql生成语句中,字段名是下划线方式 例如:recv_id,但是接口转换的数据json tag字段的风格是recvID,类似的所有的字段需要做个map映射
-//5.任何涉及到gorm获取的是否需要返回错误,比如take和find都需要在文档上说明
-//6.任何涉及到update的操作,一定要看gorm原型中实现,如果有select(*)则不需要用temp_struct中的结构体
-//7.任何和接口重名的时候,db接口统一加上后缀DB
-//8.任何map类型统一使用json字符串转换,文档说明
+// 1. Using the native wasm method, TinyGo is applied to Go's embedded domain,
+// but the supported functionality is limited and only supports a subset of Go.
+// Even JSON serialization is not supported.
+// 2. Function names should follow camelCase convention.
+// 3. In the provided SQL generation statements, boolean values need special handling.
+// For create statements, boolean values should be explicitly handled because SQLite does not natively support boolean types.
+// Instead, integers (1 or 0) are used as substitutes, and GORM converts them automatically.
+// 4. In the provided SQL generation statements, field names use snake_case (e.g., recv_id),
+// but in the interface-converted data, the JSON tag fields follow camelCase (e.g., recvID).
+// All such fields should have a mapped transformation.
+// 5. Any GORM operations that involve retrieval (e.g., take and find) should specify whether they need to return an error in the documentation.
+// 6. For any update operations, be sure to check GORM's implementation. If there is a select(*) query involved,
+// you do not need to use the structures in temp_struct.
+// 7. Whenever there's a name conflict with an interface, the DB interface should append the "DB" suffix.
+// 8. For any map types, use JSON string conversion, and document this clearly.
type IndexDB struct {
LocalUsers
LocalConversations
*LocalChatLogs
- LocalSuperGroup
LocalConversationUnreadMessages
LocalGroups
LocalGroupMember
LocalGroupRequest
- LocalCacheMessage
- LocalStrangers
LocalUserCommand
*FriendRequest
*Black
*Friend
- LocalChatLogReactionExtensions
loginUserID string
}
@@ -62,40 +65,4 @@ func (i IndexDB) InitDB(ctx context.Context, userID string, dataDir string) erro
}
func (i IndexDB) SetChatLogFailedStatus(ctx context.Context) {
- //msgList, err := i.GetSendingMessageList()
- //if err != nil {
- // log.Error("", "GetSendingMessageList failed ", err.Error())
- // return
- //}
- //for _, v := range msgList {
- // v.Status = constant.MsgStatusSendFailed
- // err := i.UpdateMessage(v)
- // if err != nil {
- // log.Error("", "UpdateMessage failed ", err.Error(), v)
- // continue
- // }
- //}
- //groupIDList, err := i.GetReadDiffusionGroupIDList()
- //if err != nil {
- // log.Error("", "GetReadDiffusionGroupIDList failed ", err.Error())
- // return
- //}
- //for _, v := range groupIDList {
- // msgList, err := i.SuperGroupGetSendingMessageList(v)
- // if err != nil {
- // log.Error("", "GetSendingMessageList failed ", err.Error())
- // return
- // }
- // if len(msgList) > 0 {
- // for _, v := range msgList {
- // v.Status = constant.MsgStatusSendFailed
- // err := i.SuperGroupUpdateMessage(v)
- // if err != nil {
- // log.Error("", "UpdateMessage failed ", err.Error(), v)
- // continue
- // }
- // }
- // }
- //
- //}
}
diff --git a/wasm/indexdb/stranger_model.go b/wasm/indexdb/stranger_model.go
deleted file mode 100644
index 049a434ab..000000000
--- a/wasm/indexdb/stranger_model.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build js && wasm
-// +build js,wasm
-
-package indexdb
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
-)
-
-import (
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
-)
-
-type LocalStrangers struct {
-}
-
-func NewLocalStrangers() *LocalStrangers {
- return &LocalStrangers{}
-}
-
-func (l *LocalStrangers) GetStrangerInfo(ctx context.Context, userIDs []string) (result []*model_struct.LocalStranger, err error) {
- gList, err := exec.Exec(utils.StructToJsonString(userIDs))
- if err != nil {
- return nil, err
- } else {
- if v, ok := gList.(string); ok {
- var temp []model_struct.LocalStranger
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-func (l *LocalStrangers) SetStrangerInfo(ctx context.Context, strangerList []*model_struct.LocalStranger) error {
- _, err := exec.Exec(utils.StructToJsonString(strangerList))
- return err
-}
diff --git a/wasm/indexdb/super_group_model.go b/wasm/indexdb/super_group_model.go
deleted file mode 100644
index 34384fcd1..000000000
--- a/wasm/indexdb/super_group_model.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright © 2023 OpenIM SDK. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//go:build js && wasm
-// +build js,wasm
-
-package indexdb
-
-import (
- "context"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
- "github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
- "github.com/openimsdk/openim-sdk-core/v3/wasm/exec"
-)
-
-type LocalSuperGroup struct{}
-
-func NewLocalSuperGroup() *LocalSuperGroup {
- return &LocalSuperGroup{}
-}
-
-func (i *LocalSuperGroup) GetJoinedSuperGroupList(ctx context.Context) (result []*model_struct.LocalGroup, err error) {
- groupList, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := groupList.(string); ok {
- var temp []model_struct.LocalGroup
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-func (i *LocalSuperGroup) InsertSuperGroup(ctx context.Context, groupInfo *model_struct.LocalGroup) error {
- _, err := exec.Exec(utils.StructToJsonString(groupInfo))
- return err
-}
-func (i *LocalSuperGroup) UpdateSuperGroup(ctx context.Context, g *model_struct.LocalGroup) error {
- _, err := exec.Exec(g.GroupID, utils.StructToJsonString(g))
- return err
-}
-
-func (i *LocalSuperGroup) DeleteSuperGroup(ctx context.Context, groupID string) error {
- _, err := exec.Exec(groupID)
- return err
-}
-
-func (i *LocalSuperGroup) DeleteAllSuperGroup(ctx context.Context) error {
- _, err := exec.Exec()
- return err
-}
-
-func (i *LocalSuperGroup) GetSuperGroupInfoByGroupID(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
- groupInfo, err := exec.Exec(groupID)
- if err != nil {
- return nil, err
- } else {
- if v, ok := groupInfo.(string); ok {
- result := model_struct.LocalGroup{}
- err := utils.JsonStringToStruct(v, &result)
- if err != nil {
- return nil, err
- }
- return &result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
-
-func (i *LocalSuperGroup) GetJoinedWorkingGroupIDList(ctx context.Context) ([]string, error) {
- IDList, err := exec.Exec()
- if err != nil {
- return nil, err
- }
- if v, ok := IDList.(string); ok {
- var temp []string
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- return temp, nil
- }
- return nil, exec.ErrType
-}
-
-func (i *LocalSuperGroup) GetJoinedWorkingGroupList(ctx context.Context) (result []*model_struct.LocalGroup, err error) {
- groupList, err := exec.Exec()
- if err != nil {
- return nil, err
- } else {
- if v, ok := groupList.(string); ok {
- var temp []model_struct.LocalGroup
- err := utils.JsonStringToStruct(v, &temp)
- if err != nil {
- return nil, err
- }
- for _, v := range temp {
- v1 := v
- result = append(result, &v1)
- }
- return result, err
- } else {
- return nil, exec.ErrType
- }
- }
-}
diff --git a/wasm/indexdb/temp_struct/struct.go b/wasm/indexdb/temp_struct/struct.go
index 8b9bc9ae7..d62012f3f 100644
--- a/wasm/indexdb/temp_struct/struct.go
+++ b/wasm/indexdb/temp_struct/struct.go
@@ -73,27 +73,6 @@ type LocalPartConversation struct {
Ex string ` json:"ex"`
}
-type LocalSuperGroup struct {
- GroupID string `json:"groupID,omitempty"`
- GroupName string `json:"groupName,omitempty"`
- Notification string `json:"notification,omitempty"`
- Introduction string `json:"introduction,omitempty"`
- FaceURL string `json:"faceURL,omitempty"`
- CreateTime uint32 `json:"createTime,omitempty"`
- Status int32 `json:"status,omitempty"`
- CreatorUserID string `json:"creatorUserID,omitempty"`
- GroupType int32 `json:"groupType,omitempty"`
- OwnerUserID string `json:"ownerUserID,omitempty"`
- MemberCount int32 `json:"memberCount,omitempty"`
- Ex string `json:"ex,omitempty"`
- AttachedInfo string `json:"attachedInfo,omitempty"`
- NeedVerification int32 `json:"needVerification,omitempty"`
- LookMemberInfo int32 `json:"lookMemberInfo,omitempty"`
- ApplyMemberFriend int32 `json:"applyMemberFriend,omitempty"`
- NotificationUpdateTime uint32 `json:"notificationUpdateTime,omitempty"`
- NotificationUserID string `json:"notificationUserID,omitempty"`
-}
-
type LocalGroup struct {
GroupID string `json:"groupID,omitempty"`
GroupName string `json:"groupName,omitempty"`
diff --git a/wasm/wasm_wrapper/wasm_conversation_msg.go b/wasm/wasm_wrapper/wasm_conversation_msg.go
index 1da0bd059..ce62f5553 100644
--- a/wasm/wasm_wrapper/wasm_conversation_msg.go
+++ b/wasm/wasm_wrapper/wasm_conversation_msg.go
@@ -18,10 +18,11 @@
package wasm_wrapper
import (
+ "syscall/js"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/wasm/event_listener"
- "syscall/js"
)
// ------------------------------------message---------------------------
@@ -39,18 +40,6 @@ func (w *WrapperConMsg) CreateTextMessage(_ js.Value, args []js.Value) interface
func (w *WrapperConMsg) CreateImageMessage(_ js.Value, args []js.Value) interface{} {
return event_listener.NewCaller(open_im_sdk.CreateImageMessage, nil, &args).AsyncCallWithOutCallback()
}
-func (w *WrapperConMsg) CreateImageMessageByURL(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateImageMessageByURL, nil, &args).AsyncCallWithOutCallback()
-}
-func (w *WrapperConMsg) CreateSoundMessageByURL(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateSoundMessageByURL, nil, &args).AsyncCallWithOutCallback()
-}
-func (w *WrapperConMsg) CreateVideoMessageByURL(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateVideoMessageByURL, nil, &args).AsyncCallWithOutCallback()
-}
-func (w *WrapperConMsg) CreateFileMessageByURL(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateFileMessageByURL, nil, &args).AsyncCallWithOutCallback()
-}
func (w *WrapperConMsg) CreateCustomMessage(_ js.Value, args []js.Value) interface{} {
return event_listener.NewCaller(open_im_sdk.CreateCustomMessage, nil, &args).AsyncCallWithOutCallback()
}
@@ -94,21 +83,6 @@ func (w *WrapperConMsg) CreateLocationMessage(_ js.Value, args []js.Value) inter
return event_listener.NewCaller(open_im_sdk.CreateLocationMessage, nil, &args).AsyncCallWithOutCallback()
}
-func (w *WrapperConMsg) CreateVideoMessageFromFullPath(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateVideoMessageFromFullPath, nil, &args).AsyncCallWithOutCallback()
-}
-
-func (w *WrapperConMsg) CreateImageMessageFromFullPath(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateImageMessageFromFullPath, nil, &args).AsyncCallWithOutCallback()
-}
-func (w *WrapperConMsg) CreateSoundMessageFromFullPath(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateSoundMessageFromFullPath, nil, &args).AsyncCallWithOutCallback()
-}
-
-func (w *WrapperConMsg) CreateFileMessageFromFullPath(_ js.Value, args []js.Value) interface{} {
- return event_listener.NewCaller(open_im_sdk.CreateFileMessageFromFullPath, nil, &args).AsyncCallWithOutCallback()
-}
-
func (w *WrapperConMsg) CreateSoundMessage(_ js.Value, args []js.Value) interface{} {
return event_listener.NewCaller(open_im_sdk.CreateSoundMessage, nil, &args).AsyncCallWithOutCallback()
}
@@ -121,6 +95,12 @@ func (w *WrapperConMsg) MarkConversationMessageAsRead(_ js.Value, args []js.Valu
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.MarkConversationMessageAsRead, callback, &args).AsyncCallWithCallback()
}
+
+func (w *WrapperConMsg) MarkAllConversationMessageAsRead(_ js.Value, args []js.Value) interface{} {
+ callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
+ return event_listener.NewCaller(open_im_sdk.MarkAllConversationMessageAsRead, callback, &args).AsyncCallWithCallback()
+}
+
func (w *WrapperConMsg) MarkMessagesAsReadByMsgID(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.MarkMessagesAsReadByMsgID, callback, &args).AsyncCallWithCallback()
@@ -129,29 +109,25 @@ func (w *WrapperConMsg) SendMessage(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewSendMessageCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc).SetClientMsgID(&args)
return event_listener.NewCaller(open_im_sdk.SendMessage, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperConMsg) SendMessageNotOss(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewSendMessageCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc).SetClientMsgID(&args)
- return event_listener.NewCaller(open_im_sdk.SendMessageNotOss, callback, &args).AsyncCallWithCallback()
-}
-//funcation (w *WrapperConMsg) SetMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) SetMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.SetMessageReactionExtensions, callback, &args).AsyncCallWithCallback()
//}
-//funcation (w *WrapperConMsg) AddMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) AddMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.AddMessageReactionExtensions, callback, &args).AsyncCallWithCallback()
//}
//
-//funcation (w *WrapperConMsg) DeleteMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) DeleteMessageReactionExtensions(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.DeleteMessageReactionExtensions, callback, &args).AsyncCallWithCallback()
//}
-//funcation (w *WrapperConMsg) GetMessageListReactionExtensions(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) GetMessageListReactionExtensions(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.GetMessageListReactionExtensions, callback, &args).AsyncCallWithCallback()
//}
-//funcation (w *WrapperConMsg) GetMessageListSomeReactionExtensions(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) GetMessageListSomeReactionExtensions(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.GetMessageListSomeReactionExtensions, callback, &args).AsyncCallWithCallback()
//}
@@ -183,7 +159,7 @@ func (w *WrapperConMsg) GetAdvancedHistoryMessageListReverse(_ js.Value, args []
return event_listener.NewCaller(open_im_sdk.GetAdvancedHistoryMessageListReverse, callback, &args).AsyncCallWithCallback()
}
-//funcation (w *WrapperConMsg) GetHistoryMessageList(_ js.Value, args []js.Value) interface{} {
+//func (w *WrapperConMsg) GetHistoryMessageList(_ js.Value, args []js.Value) interface{} {
// callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
// return event_listener.NewCaller(open_im_sdk.GetHistoryMessageList, callback, &args).AsyncCallWithCallback()
//}
@@ -193,24 +169,6 @@ func (w *WrapperConMsg) GetMultipleConversation(_ js.Value, args []js.Value) int
return event_listener.NewCaller(open_im_sdk.GetMultipleConversation, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperConMsg) SetConversationPrivateChat(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetConversationPrivateChat, callback, &args).AsyncCallWithCallback()
-}
-
-func (w *WrapperConMsg) SetConversationRecvMessageOpt(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetConversationRecvMessageOpt, callback, &args).AsyncCallWithCallback()
-}
-func (w *WrapperConMsg) SearchConversation(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SearchConversation, callback, &args).AsyncCallWithCallback()
-}
-func (w *WrapperConMsg) SetGlobalRecvMessageOpt(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGlobalRecvMessageOpt, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperConMsg) FindMessageList(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.FindMessageList, callback, &args).AsyncCallWithCallback()
@@ -275,6 +233,11 @@ func (w *WrapperConMsg) SetMessageLocalEx(_ js.Value, args []js.Value) interface
return event_listener.NewCaller(open_im_sdk.SetMessageLocalEx, callback, &args).AsyncCallWithCallback()
}
+func (w *WrapperConMsg) SearchConversation(_ js.Value, args []js.Value) interface{} {
+ callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
+ return event_listener.NewCaller(open_im_sdk.SearchConversation, callback, &args).AsyncCallWithCallback()
+}
+
func (w *WrapperConMsg) DeleteConversationAndDeleteAllMsg(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.DeleteConversationAndDeleteAllMsg, callback, &args).AsyncCallWithCallback()
@@ -284,36 +247,20 @@ func (w *WrapperConMsg) HideConversation(_ js.Value, args []js.Value) interface{
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.HideConversation, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperConMsg) SetConversationEx(_ js.Value, args []js.Value) interface{} {
+func (w *WrapperConMsg) SetConversation(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetConversationEx, callback, &args).AsyncCallWithCallback()
+ return event_listener.NewCaller(open_im_sdk.SetConversation, callback, &args).AsyncCallWithCallback()
}
func (w *WrapperConMsg) SetConversationDraft(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.SetConversationDraft, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperConMsg) ResetConversationGroupAtType(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.ResetConversationGroupAtType, callback, &args).AsyncCallWithCallback()
-}
-
-func (w *WrapperConMsg) PinConversation(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
-
- return event_listener.NewCaller(open_im_sdk.PinConversation, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperConMsg) GetTotalUnreadMsgCount(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.GetTotalUnreadMsgCount, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperConMsg) SetConversationBurnDuration(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetConversationBurnDuration, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperConMsg) ChangeInputStates(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.ChangeInputStates, callback, &args).AsyncCallWithCallback()
diff --git a/wasm/wasm_wrapper/wasm_friend.go b/wasm/wasm_wrapper/wasm_friend.go
index ed7d3fae0..657328069 100644
--- a/wasm/wasm_wrapper/wasm_friend.go
+++ b/wasm/wasm_wrapper/wasm_friend.go
@@ -64,14 +64,11 @@ func (w *WrapperFriend) AddFriend(_ js.Value, args []js.Value) interface{} {
return event_listener.NewCaller(open_im_sdk.AddFriend, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperFriend) SetFriendRemark(_ js.Value, args []js.Value) interface{} {
+func (w *WrapperFriend) UpdateFriends(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetFriendRemark, callback, &args).AsyncCallWithCallback()
-}
-func (w *WrapperFriend) PinFriends(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.PinFriends, callback, &args).AsyncCallWithCallback()
+ return event_listener.NewCaller(open_im_sdk.UpdateFriends, callback, &args).AsyncCallWithCallback()
}
+
func (w *WrapperFriend) DeleteFriend(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.DeleteFriend, callback, &args).AsyncCallWithCallback()
@@ -111,7 +108,3 @@ func (w *WrapperFriend) AddBlack(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.AddBlack, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperFriend) SetFriendsEx(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetFriendsEx, callback, &args).AsyncCallWithCallback()
-}
diff --git a/wasm/wasm_wrapper/wasm_group.go b/wasm/wasm_wrapper/wasm_group.go
index 159e89472..d31222755 100644
--- a/wasm/wasm_wrapper/wasm_group.go
+++ b/wasm/wasm_wrapper/wasm_group.go
@@ -64,11 +64,6 @@ func (w *WrapperGroup) ChangeGroupMemberMute(_ js.Value, args []js.Value) interf
return event_listener.NewCaller(open_im_sdk.ChangeGroupMemberMute, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperGroup) SetGroupMemberRoleLevel(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGroupMemberRoleLevel, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperGroup) SetGroupMemberInfo(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.SetGroupMemberInfo, callback, &args).AsyncCallWithCallback()
@@ -94,21 +89,6 @@ func (w *WrapperGroup) SetGroupInfo(_ js.Value, args []js.Value) interface{} {
return event_listener.NewCaller(open_im_sdk.SetGroupInfo, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperGroup) SetGroupVerification(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGroupVerification, callback, &args).AsyncCallWithCallback()
-}
-
-func (w *WrapperGroup) SetGroupLookMemberInfo(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGroupLookMemberInfo, callback, &args).AsyncCallWithCallback()
-}
-
-func (w *WrapperGroup) SetGroupApplyMemberFriend(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGroupApplyMemberFriend, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperGroup) GetGroupMemberList(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.GetGroupMemberList, callback, &args).AsyncCallWithCallback()
@@ -164,11 +144,6 @@ func (w *WrapperGroup) RefuseGroupApplication(_ js.Value, args []js.Value) inter
return event_listener.NewCaller(open_im_sdk.RefuseGroupApplication, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperGroup) SetGroupMemberNickname(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetGroupMemberNickname, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperGroup) SearchGroupMembers(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.SearchGroupMembers, callback, &args).AsyncCallWithCallback()
diff --git a/wasm/wasm_wrapper/wasm_init_login.go b/wasm/wasm_wrapper/wasm_init_login.go
index 9e2ee3a1d..90e6b4a87 100644
--- a/wasm/wasm_wrapper/wasm_init_login.go
+++ b/wasm/wasm_wrapper/wasm_init_login.go
@@ -19,16 +19,17 @@ package wasm_wrapper
import (
"errors"
+ "syscall/js"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/wasm/event_listener"
- "syscall/js"
)
const COMMONEVENTFUNC = "commonEventFunc"
var ErrArgsLength = errors.New("from javascript args length err")
-var ErrFunNameNotSet = errors.New("reflect funcation not to set")
+var ErrFunNameNotSet = errors.New("reflect func not to set")
type SetListener struct {
*WrapperCommon
@@ -47,11 +48,6 @@ func (s *SetListener) setAdvancedMsgListener() {
open_im_sdk.SetAdvancedMsgListener(callback)
}
-func (s *SetListener) setBatchMessageListener() {
- callback := event_listener.NewBatchMessageCallback(s.commonFunc)
- open_im_sdk.SetBatchMsgListener(callback)
-}
-
func (s *SetListener) setFriendListener() {
callback := event_listener.NewFriendCallback(s.commonFunc)
open_im_sdk.SetFriendListener(callback)
@@ -79,7 +75,6 @@ func (s *SetListener) setCustomBusinessListener() {
func (s *SetListener) SetAllListener() {
s.setConversationListener()
s.setAdvancedMsgListener()
- s.setBatchMessageListener()
s.setFriendListener()
s.setGroupListener()
s.setUserListener()
diff --git a/wasm/wasm_wrapper/wasm_user.go b/wasm/wasm_wrapper/wasm_user.go
index a7aec45ca..6180842cb 100644
--- a/wasm/wasm_wrapper/wasm_user.go
+++ b/wasm/wasm_wrapper/wasm_user.go
@@ -18,10 +18,11 @@
package wasm_wrapper
import (
+ "syscall/js"
+
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/wasm/event_listener"
- "syscall/js"
)
// ------------------------------------group---------------------------
@@ -42,20 +43,12 @@ func (w *WrapperUser) SetSelfInfo(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.SetSelfInfo, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperUser) SetSelfInfoEx(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.SetSelfInfoEx, callback, &args).AsyncCallWithCallback()
-}
+
func (w *WrapperUser) GetUsersInfo(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.GetUsersInfo, callback, &args).AsyncCallWithCallback()
}
-func (w *WrapperUser) GetUsersInfoWithCache(_ js.Value, args []js.Value) interface{} {
- callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
- return event_listener.NewCaller(open_im_sdk.GetUsersInfoWithCache, callback, &args).AsyncCallWithCallback()
-}
-
func (w *WrapperUser) SubscribeUsersStatus(_ js.Value, args []js.Value) interface{} {
callback := event_listener.NewBaseCallback(utils.FirstLower(utils.GetSelfFuncName()), w.commonFunc)
return event_listener.NewCaller(open_im_sdk.SubscribeUsersStatus, callback, &args).AsyncCallWithCallback()