Merge branch 'openimsdk:main' into main

pull/2622/head
icey-yu 1 year ago committed by GitHub
commit 9af3ab6935
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -19,8 +19,27 @@ on:
branches: branches:
- main - main
- release-* - release-*
paths-ignore:
- "docs/**"
- "README.md"
- "README_zh-CN.md"
- "**.md"
- "docs/**"
- "CONTRIBUTING.md"
tags: tags:
- v* - v*
pull_request:
types: [closed]
branches:
- main
- release-*
paths-ignore:
- "docs/**"
- "README.md"
- "README_zh-CN.md"
- "**.md"
- "docs/**"
- "CONTRIBUTING.md"
workflow_dispatch: workflow_dispatch:
env: env:
@ -29,109 +48,112 @@ env:
jobs: jobs:
build-dockerhub: build-dockerhub:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true)
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.merged == false) }}
steps: steps:
- name: Checkout - name: Checkout main repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
path: main-repo
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
# docker.io/openim/openim-server:latest
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5.5.1
with:
images: openim/openim-server
# generate Docker tags based on the following events/attributes
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: ./main-repo
# linux/ppc64le,linux/s390x load: true
platforms: linux/amd64,linux/arm64 tags: "openim/openim-server:local"
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-aliyun: - name: Checkout compose repository
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server:latest
- name: Extract metadata (tags, labels) for Docker
id: meta2
uses: docker/metadata-action@v5.5.1
with: with:
images: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server repository: "openimsdk/openim-docker"
# generate Docker tags based on the following events/attributes path: "compose-repo"
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Log in to AliYun Docker Hub - name: Get Internal IP Address
uses: docker/login-action@v3 id: get-ip
with: run: |
registry: registry.cn-hangzhou.aliyuncs.com IP=$(hostname -I | awk '{print $1}')
username: ${{ secrets.ALIREGISTRY_USERNAME }} echo "The IP Address is: $IP"
password: ${{ secrets.ALIREGISTRY_TOKEN }} echo "::set-output name=ip::$IP"
- name: Build and push Docker image - name: Update .env to use the local image
uses: docker/build-push-action@v5 run: |
with: sed -i 's|OPENIM_SERVER_IMAGE=.*|OPENIM_SERVER_IMAGE=openim/openim-server:local|' ${{ github.workspace }}/compose-repo/.env
context: . sed -i 's|MINIO_EXTERNAL_ADDRESS=.*|MINIO_EXTERNAL_ADDRESS=http://${{ steps.get-ip.outputs.ip }}:10005|' ${{ github.workspace }}/compose-repo/.env
# linux/ppc64le,linux/s390x
platforms: linux/amd64,linux/arm64 - name: Start services using Docker Compose
push: ${{ github.event_name != 'pull_request' }} run: |
tags: ${{ steps.meta2.outputs.tags }} cd ${{ github.workspace }}/compose-repo
labels: ${{ steps.meta2.outputs.labels }} docker compose up -d
sleep 30
- name: Check openim-server health
run: |
timeout=300
interval=30
elapsed=0
while [[ $elapsed -le $timeout ]]; do
if ! docker exec openim-server mage check; then
echo "openim-server is not ready, waiting..."
sleep $interval
elapsed=$(($elapsed + $interval))
else
echo "Health check successful"
exit 0
fi
done
echo "Health check failed after 5 minutes"
exit 1
- name: Check openim-chat health
if: success()
run: |
if ! docker exec openim-chat mage check; then
echo "openim-chat check failed"
exit 1
else
echo "Health check successful"
exit 0
fi
# - name: Checkout e2e
# if: success()
# uses: actions/checkout@v4
# with:
# repository: "openimsdk/test-e2e"
# path: e2e-repo
# - name: Set up Python 3.9
# uses: actions/setup-python@v4
# with:
# python-version: '3.9'
# - name: Install dependencies
# run: |
# sudo apt-get update
# sudo apt-get install -y xvfb libxi6 libgconf-2-4
# cd ${{ github.workspace }}/e2e-repo
# pip install -r requirements.txt
# - name: Run tests
# run: |
# cd ${{ github.workspace }}/e2e-repo
# xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' pytest -v -s ./script
build-ghcr:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# ghcr.io/openimsdk/openim-server:latest
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker
id: meta3 if: success()
id: meta
uses: docker/metadata-action@v5.5.1 uses: docker/metadata-action@v5.5.1
with: with:
images: ghcr.io/openimsdk/openim-server images: |
openim/openim-server
ghcr.io/openimsdk/openim-server
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server
# generate Docker tags based on the following events/attributes # generate Docker tags based on the following events/attributes
tags: | tags: |
type=ref,event=tag type=ref,event=tag
@ -144,19 +166,33 @@ jobs:
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=sha type=sha
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Aliyun Container Registry
uses: docker/login-action@v2
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALIREGISTRY_USERNAME }}
password: ${{ secrets.ALIREGISTRY_TOKEN }}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: ./main-repo
push: true
# linux/ppc64le,linux/s390x # linux/ppc64le,linux/s390x
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }}
tags: ${{ steps.meta3.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta3.outputs.labels }}

@ -1,53 +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: Github Rebot for Link check error
# Every Monday at 12:30 p.m
on:
schedule:
- cron: '30 12 * * 1'
jobs:
linkChecker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Link Checker
id: lychee
uses: lycheeverse/lychee-action@v1.9.3
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 <timeout> Website timeout in seconds from connect to response finished [default:20]
# --max-concurrency <max-concurrency> Maximum number of concurrent network requests [default: 128]
# -a --accept <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.BOT_GITHUB_TOKEN}}
- name: Create Issue From File
if: env.lychee_exit_code != 0
uses: peter-evans/create-issue-from-file@v5
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 }}

@ -71,6 +71,18 @@ jobs:
run: sudo bash bootstrap.sh run: sudo bash bootstrap.sh
timeout-minutes: 20 timeout-minutes: 20
# - name: Get Internal IP Address
# id: get-ip
# run: |
# IP=$(hostname -I | awk '{print $1}')
# echo "The IP Address is: $IP"
# echo "::set-output name=ip::$IP"
# - name: Update .env
# run: |
# sed -i 's|externalAddress:.*|externalAddress: "http://${{ steps.get-ip.outputs.ip }}:10005"|' config/minio.yml
# cat config/minio.yml
- name: Build, Start, Check Services and Print Logs for Linux - name: Build, Start, Check Services and Print Logs for Linux
run: | run: |
sudo mage sudo mage
@ -84,108 +96,40 @@ jobs:
sudo mage start sudo mage start
sudo mage check sudo mage check
- name: Checkout chat repository
uses: actions/checkout@v4
with:
repository: 'openimsdk/chat'
path: 'chat-repo'
- name: Build and Start Chat Services
run: |
cd ${{ github.workspace }}/chat-repo
sudo mage
sudo mage start
sudo mage check
# - name: Checkout e2e repository
# uses: actions/checkout@v4
# with:
# repository: "openimsdk/test-e2e"
# path: e2e-repo
# - name: Set up Python 3.9
# uses: actions/setup-python@v4
# with:
# python-version: '3.9'
# - name: Install dependencies
# run: |
# sudo apt-get update
# sudo apt-get install -y xvfb libxi6 libgconf-2-4
# cd ${{ github.workspace }}/e2e-repo
# pip install -r requirements.txt
# - name: Run tests
# run: |
# cd ${{ github.workspace }}/e2e-repo
# xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' pytest -v -s ./script
# build-mac:
# name: Execute OpenIM Script On macOS
# runs-on: macos-latest
# permissions:
# contents: write
# pull-requests: write
# environment:
# name: openim
# strategy:
# matrix:
# arch: [arm64, armv7, amd64]
#
# steps:
# - uses: actions/checkout@v3
# - name: Set up Go
# uses: actions/setup-go@v4
# with:
# go-version: '1.21'
# while ! docker system info > /dev/null 2>&1; do
# echo "Waiting for Docker to start..."
# sleep 10 # Increased delay to ensure Docker starts properly
# done
# - name: Install Docker
# run: |
# brew install docker
# brew install docker-compose
# sleep 10
# docker-compose up -d
# sleep 30
# timeout-minutes: 20
#
# - name: init
# run: sudo bash bootstrap.sh
# timeout-minutes: 20
# - name: Build, Start, Check Services and Print Logs for Linux
# run: |
# sudo mage
# sudo mage start
# sudo mage check
# - name: Restart Services and Print Logs
# run: |
# sudo mage stop
# sudo mage start
# sudo mage check
# build-windows:
# name: Execute OpenIM Script On Windows
# runs-on: windows-latest
# permissions:
# contents: write
# pull-requests: write
# environment:
# name: openim
# strategy:
# matrix:
# arch: [arm64, armv7, amd64]
#
# steps:
# - uses: actions/checkout@v3
# - name: Set up Go
# uses: actions/setup-go@v4
# with:
# go-version: '1.21'
# - name: Set up Docker for Windows
# run: |
# $images = @("zookeeper", "redis", "kafka")
# foreach ($image in $images) {
# $tag = "$image:latest"
# docker pull $tag | Out-Null
# if ($LASTEXITCODE -ne 0) {
# Write-Host "Skipping $image as it is not available for Windows"
# } else {
# Write-Host "Successfully pulled $image"
# }
# }
# docker compose up -d
# Start-Sleep -Seconds 30
# timeout-minutes: 20
# shell: pwsh
# - name: init
# run: bootstrap.bat
# timeout-minutes: 20
# - name: Build, Start, Check Services and Print Logs for Linux
# run: |
# mage
# mage start
# mage check
# - name: Restart Services and Print Logs
# run: |
# mage stop
# mage start
# mage check

@ -1,48 +0,0 @@
# Copyright © 2023 OpenIM. 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 * * 1'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.BOT_GITHUB_TOKEN }}
days-before-stale: 60
days-before-close: 305
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

@ -1,56 +0,0 @@
# Version logging for OpenIM
> **Note**:
> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md)
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [Version logging for OpenIM](#version-logging-for-openim)
- [Unreleased](#unreleased)
- [v1.0.7 - 2021-12-17](#v107---2021-12-17)
- [v1.0.6 - 2021-12-10](#v106---2021-12-10)
- [v1.0.5 - 2021-12-03](#v105---2021-12-03)
- [v1.0.4 - 2021-11-25](#v104---2021-11-25)
- [v1.0.3 - 2021-11-12](#v103---2021-11-12)
- [v1.0.1 - 2021-11-04](#v101---2021-11-04)
- [v1.0.0 - 2021-10-28](#v100---2021-10-28)
- [Reverts](#reverts)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v1.0.7"></a>
## [v1.0.7] - 2021-12-17
<a name="v1.0.6"></a>
## [v1.0.6] - 2021-12-10
<a name="v1.0.5"></a>
## [v1.0.5] - 2021-12-03
<a name="v1.0.4"></a>
## [v1.0.4] - 2021-11-25
<a name="v1.0.3"></a>
## [v1.0.3] - 2021-11-12
<a name="v1.0.1"></a>
## [v1.0.1] - 2021-11-04
<a name="v1.0.0"></a>
## v1.0.0 - 2021-10-28
### Reverts
- friend modify
- update
[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v1.0.7...HEAD
[v1.0.7]: https://github.com/openimsdk/open-im-server/compare/v1.0.6...v1.0.7
[v1.0.6]: https://github.com/openimsdk/open-im-server/compare/v1.0.5...v1.0.6
[v1.0.5]: https://github.com/openimsdk/open-im-server/compare/v1.0.4...v1.0.5
[v1.0.4]: https://github.com/openimsdk/open-im-server/compare/v1.0.3...v1.0.4
[v1.0.3]: https://github.com/openimsdk/open-im-server/compare/v1.0.1...v1.0.3
[v1.0.1]: https://github.com/openimsdk/open-im-server/compare/v1.0.0...v1.0.1

@ -1,87 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.0.10"></a>
## [v2.0.10] - 2022-05-13
<a name="v2.0.9"></a>
## [v2.0.9] - 2022-04-29
### Reverts
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.8"></a>
## [v2.0.8] - 2022-04-24
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.7"></a>
## [v2.0.7] - 2022-04-08
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.6"></a>
## [v2.0.6] - 2022-04-01
### Pull Requests
- Merge branch 'tuoyun'
<a name="v2.0.5"></a>
## [v2.0.5] - 2022-03-24
<a name="v2.04"></a>
## [v2.04] - 2022-03-18
<a name="v2.0.3"></a>
## [v2.0.3] - 2022-03-11
<a name="v2.0.2"></a>
## [v2.0.2] - 2022-03-04
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.1"></a>
## [v2.0.1] - 2022-02-25
<a name="v2.0.0"></a>
## v2.0.0 - 2022-02-23
### Reverts
- friend modify
- update
[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v2.0.10...HEAD
[v2.0.10]: https://github.com/openimsdk/open-im-server/compare/v2.0.9...v2.0.10
[v2.0.9]: https://github.com/openimsdk/open-im-server/compare/v2.0.8...v2.0.9
[v2.0.8]: https://github.com/openimsdk/open-im-server/compare/v2.0.7...v2.0.8
[v2.0.7]: https://github.com/openimsdk/open-im-server/compare/v2.0.6...v2.0.7
[v2.0.6]: https://github.com/openimsdk/open-im-server/compare/v2.0.5...v2.0.6
[v2.0.5]: https://github.com/openimsdk/open-im-server/compare/v2.04...v2.0.5
[v2.04]: https://github.com/openimsdk/open-im-server/compare/v2.0.3...v2.04
[v2.0.3]: https://github.com/openimsdk/open-im-server/compare/v2.0.2...v2.0.3
[v2.0.2]: https://github.com/openimsdk/open-im-server/compare/v2.0.1...v2.0.2
[v2.0.1]: https://github.com/openimsdk/open-im-server/compare/v2.0.0...v2.0.1

@ -1,50 +0,0 @@
# Version logging for OpenIM:v2.1
> **Note**:
> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md)
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [Version logging for OpenIM:v2.1](#version-logging-for-openimv21)
- [Unreleased](#unreleased)
- [v2.1.0 - 2022-06-17](#v210---2022-06-17)
- [Reverts](#reverts)
- [Pull Requests](#pull-requests)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.1.0"></a>
## v2.1.0 - 2022-06-17
### Reverts
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
- friend modify
- update
### 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'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- 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/open-im-server/compare/v2.1.0...HEAD

@ -1,50 +0,0 @@
# Version logging for OpenIM:v2.2
> **Note**:
> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md)
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [Version logging for OpenIM:v2.2](#version-logging-for-openimv22)
- [Unreleased](#unreleased)
- [v2.2.0 - 2022-07-01](#v220---2022-07-01)
- [Reverts](#reverts)
- [Pull Requests](#pull-requests)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.2.0"></a>
## v2.2.0 - 2022-07-01
### Reverts
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
- friend modify
- update
### 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'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- 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/open-im-server/compare/v2.2.0...HEAD

@ -1,70 +0,0 @@
# Version logging for OpenIM:v2.3
> **Note**:
> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md)
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [Version logging for OpenIM:v2.3](#version-logging-for-openimv23)
- [Unreleased](#unreleased)
- [v2.3.3 - 2022-09-18](#v233---2022-09-18)
- [v2.3.2 - 2022-09-09](#v232---2022-09-09)
- [v2.3.0-rc2 - 2022-07-29](#v230-rc2---2022-07-29)
- [v2.3.0-rc1 - 2022-07-25](#v230-rc1---2022-07-25)
- [v2.3.0-rc0 - 2022-07-15](#v230-rc0---2022-07-15)
- [Reverts](#reverts)
- [Pull Requests](#pull-requests)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.3.3"></a>
## [v2.3.3] - 2022-09-18
<a name="v2.3.2"></a>
## [v2.3.2] - 2022-09-09
<a name="v2.3.0-rc2"></a>
## [v2.3.0-rc2] - 2022-07-29
<a name="v2.3.0-rc1"></a>
## [v2.3.0-rc1] - 2022-07-25
<a name="v2.3.0-rc0"></a>
## v2.3.0-rc0 - 2022-07-15
### Reverts
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
- friend modify
- update
### 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'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- 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/open-im-server/compare/v2.3.3...HEAD
[v2.3.3]: https://github.com/openimsdk/open-im-server/compare/v2.3.2...v2.3.3
[v2.3.2]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc2...v2.3.2
[v2.3.0-rc2]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc1...v2.3.0-rc2
[v2.3.0-rc1]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc0...v2.3.0-rc1

@ -1,52 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [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)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.9.0+1.839643f"></a>
## [v2.9.0+1.839643f] - 2023-07-07
<a name="v2.9.0+2.35f07fe"></a>
## [v2.9.0+2.35f07fe] - 2023-07-06
<a name="v2.9.0+1.b5072b1"></a>
## [v2.9.0+1.b5072b1] - 2023-07-05
<a name="v2.9.0+3.2667a3a"></a>
## [v2.9.0+3.2667a3a] - 2023-07-05
<a name="v2.9.0+7.04818ca"></a>
## [v2.9.0+7.04818ca] - 2023-07-05
<a name="v2.9.0"></a>
## 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

@ -1,188 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
- [Version logging for OpenIM](#version-logging-for-openim)
- [Unreleased](#unreleased)
- [v2.9.0 - 2023-07-04](#v290---2023-07-04)
- [Reverts](#reverts)
- [Pull Requests](#pull-requests)
- [v2.3.3 - 2022-09-18](#v233---2022-09-18)
- [v2.3.2 - 2022-09-09](#v232---2022-09-09)
- [v2.3.0-rc2 - 2022-07-29](#v230-rc2---2022-07-29)
- [v2.3.0-rc1 - 2022-07-25](#v230-rc1---2022-07-25)
- [v2.3.0-rc0 - 2022-07-15](#v230-rc0---2022-07-15)
- [v2.2.0 - 2022-07-01](#v220---2022-07-01)
- [v2.1.0 - 2022-06-17](#v210---2022-06-17)
- [Pull Requests](#pull-requests-1)
- [v2.0.10 - 2022-05-13](#v2010---2022-05-13)
- [v2.0.9 - 2022-04-29](#v209---2022-04-29)
- [Reverts](#reverts-1)
- [Pull Requests](#pull-requests-2)
- [v2.0.7 - 2022-04-08](#v207---2022-04-08)
- [Pull Requests](#pull-requests-3)
- [v2.0.6 - 2022-04-01](#v206---2022-04-01)
- [Pull Requests](#pull-requests-4)
- [v2.0.5 - 2022-03-24](#v205---2022-03-24)
- [v2.04 - 2022-03-18](#v204---2022-03-18)
- [v2.0.3 - 2022-03-11](#v203---2022-03-11)
- [v2.0.2 - 2022-03-04](#v202---2022-03-04)
- [Pull Requests](#pull-requests-5)
- [v2.0.1 - 2022-02-25](#v201---2022-02-25)
- [v2.0.0 - 2022-02-23](#v200---2022-02-23)
- [v1.0.7 - 2021-12-17](#v107---2021-12-17)
- [v1.0.6 - 2021-12-10](#v106---2021-12-10)
- [v1.0.5 - 2021-12-03](#v105---2021-12-03)
- [v1.0.4 - 2021-11-25](#v104---2021-11-25)
- [v1.0.3 - 2021-11-12](#v103---2021-11-12)
- [v1.0.1 - 2021-11-04](#v101---2021-11-04)
- [v1.0.0 - 2021-10-28](#v100---2021-10-28)
- [Reverts](#reverts-2)
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v2.9.0"></a>
## [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'
<a name="v2.3.3"></a>
## [v2.3.3] - 2022-09-18
<a name="v2.3.2"></a>
## [v2.3.2] - 2022-09-09
<a name="v2.3.0-rc2"></a>
## [v2.3.0-rc2] - 2022-07-29
<a name="v2.3.0-rc1"></a>
## [v2.3.0-rc1] - 2022-07-25
<a name="v2.3.0-rc0"></a>
## [v2.3.0-rc0] - 2022-07-15
<a name="v2.2.0"></a>
## [v2.2.0] - 2022-07-01
<a name="v2.1.0"></a>
## [v2.1.0] - 2022-06-17
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.10"></a>
## [v2.0.10] - 2022-05-13
<a name="v2.0.9"></a>
## [v2.0.9] - 2022-04-29
### Reverts
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
### 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'
<a name="v2.0.7"></a>
## [v2.0.7] - 2022-04-08
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.6"></a>
## [v2.0.6] - 2022-04-01
### Pull Requests
- Merge branch 'tuoyun'
<a name="v2.0.5"></a>
## [v2.0.5] - 2022-03-24
<a name="v2.04"></a>
## [v2.04] - 2022-03-18
<a name="v2.0.3"></a>
## [v2.0.3] - 2022-03-11
<a name="v2.0.2"></a>
## [v2.0.2] - 2022-03-04
### Pull Requests
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
- Merge branch 'tuoyun'
<a name="v2.0.1"></a>
## [v2.0.1] - 2022-02-25
<a name="v2.0.0"></a>
## [v2.0.0] - 2022-02-23
<a name="v1.0.7"></a>
## [v1.0.7] - 2021-12-17
<a name="v1.0.6"></a>
## [v1.0.6] - 2021-12-10
<a name="v1.0.5"></a>
## [v1.0.5] - 2021-12-03
<a name="v1.0.4"></a>
## [v1.0.4] - 2021-11-25
<a name="v1.0.3"></a>
## [v1.0.3] - 2021-11-12
<a name="v1.0.1"></a>
## [v1.0.1] - 2021-11-04
<a name="v1.0.0"></a>
## v1.0.0 - 2021-10-28
### Reverts
- friend modify
- update
[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v2.9.0...HEAD
[v2.9.0]: https://github.com/openimsdk/open-im-server/compare/v2.3.3...v2.9.0
[v2.3.3]: https://github.com/openimsdk/open-im-server/compare/v2.3.2...v2.3.3
[v2.3.2]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc2...v2.3.2
[v2.3.0-rc2]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc1...v2.3.0-rc2
[v2.3.0-rc1]: https://github.com/openimsdk/open-im-server/compare/v2.3.0-rc0...v2.3.0-rc1
[v2.3.0-rc0]: https://github.com/openimsdk/open-im-server/compare/v2.2.0...v2.3.0-rc0
[v2.2.0]: https://github.com/openimsdk/open-im-server/compare/v2.1.0...v2.2.0
[v2.1.0]: https://github.com/openimsdk/open-im-server/compare/v2.0.10...v2.1.0
[v2.0.10]: https://github.com/openimsdk/open-im-server/compare/v2.0.9...v2.0.10
[v2.0.9]: https://github.com/openimsdk/open-im-server/compare/v2.0.7...v2.0.9
[v2.0.7]: https://github.com/openimsdk/open-im-server/compare/v2.0.6...v2.0.7
[v2.0.6]: https://github.com/openimsdk/open-im-server/compare/v2.0.5...v2.0.6
[v2.0.5]: https://github.com/openimsdk/open-im-server/compare/v2.04...v2.0.5
[v2.04]: https://github.com/openimsdk/open-im-server/compare/v2.0.3...v2.04
[v2.0.3]: https://github.com/openimsdk/open-im-server/compare/v2.0.2...v2.0.3
[v2.0.2]: https://github.com/openimsdk/open-im-server/compare/v2.0.1...v2.0.2
[v2.0.1]: https://github.com/openimsdk/open-im-server/compare/v2.0.0...v2.0.1
[v2.0.0]: https://github.com/openimsdk/open-im-server/compare/v1.0.7...v2.0.0
[v1.0.7]: https://github.com/openimsdk/open-im-server/compare/v1.0.6...v1.0.7
[v1.0.6]: https://github.com/openimsdk/open-im-server/compare/v1.0.5...v1.0.6
[v1.0.5]: https://github.com/openimsdk/open-im-server/compare/v1.0.4...v1.0.5
[v1.0.4]: https://github.com/openimsdk/open-im-server/compare/v1.0.3...v1.0.4
[v1.0.3]: https://github.com/openimsdk/open-im-server/compare/v1.0.1...v1.0.3
[v1.0.1]: https://github.com/openimsdk/open-im-server/compare/v1.0.0...v1.0.1

@ -1,20 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.1.0"></a>
## v3.1.0 - 2023-07-28
### 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/v3.1.0...HEAD

@ -1,32 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.2.2-alpha.0"></a>
## [v3.2.2-alpha.0] - 2023-08-25
<a name="v3.2.0"></a>
## [v3.2.0] - 2023-08-19
<a name="v3.2.0-rc.0"></a>
## [v3.2.0-rc.0] - 2023-08-17
<a name="v3.2.0-alpha.0"></a>
## v3.2.0-alpha.0 - 2023-08-16
### 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/v3.2.2-alpha.0...HEAD
[v3.2.2-alpha.0]: https://github.com/openimsdk/open-im-server/compare/v3.2.0...v3.2.2-alpha.0
[v3.2.0]: https://github.com/openimsdk/open-im-server/compare/v3.2.0-rc.0...v3.2.0
[v3.2.0-rc.0]: https://github.com/openimsdk/open-im-server/compare/v3.2.0-alpha.0...v3.2.0-rc.0

@ -1,40 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.3.1"></a>
## [v3.3.1] - 2023-09-13
<a name="v3.3.1-beta.0"></a>
## [v3.3.1-beta.0] - 2023-09-11
<a name="v3.3.0-rc.1"></a>
## [v3.3.0-rc.1] - 2023-09-11
<a name="v3.3.0-rc.12"></a>
## [v3.3.0-rc.12] - 2023-09-11
<a name="v3.3.0"></a>
## [v3.3.0] - 2023-09-09
<a name="v3.3.0-rc.0"></a>
## v3.3.0-rc.0 - 2023-09-07
### 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/v3.3.1...HEAD
[v3.3.1]: https://github.com/openimsdk/open-im-server/compare/v3.3.1-beta.0...v3.3.1
[v3.3.1-beta.0]: https://github.com/openimsdk/open-im-server/compare/v3.3.0-rc.1...v3.3.1-beta.0
[v3.3.0-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.3.0-rc.12...v3.3.0-rc.1
[v3.3.0-rc.12]: https://github.com/openimsdk/open-im-server/compare/v3.3.0...v3.3.0-rc.12
[v3.3.0]: https://github.com/openimsdk/open-im-server/compare/v3.3.0-rc.0...v3.3.0

@ -1,32 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.4.2"></a>
## [v3.4.2] - 2023-12-14
<a name="v3.4.0"></a>
## [v3.4.0] - 2023-11-10
<a name="v3.4.0-rc.1"></a>
## [v3.4.0-rc.1] - 2023-11-09
<a name="v3.4.0-rc.0"></a>
## v3.4.0-rc.0 - 2023-11-09
### 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/v3.4.2...HEAD
[v3.4.2]: https://github.com/openimsdk/open-im-server/compare/v3.4.0...v3.4.2
[v3.4.0]: https://github.com/openimsdk/open-im-server/compare/v3.4.0-rc.1...v3.4.0
[v3.4.0-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.4.0-rc.0...v3.4.0-rc.1

@ -1,84 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.5.1-alpha.2"></a>
## [v3.5.1-alpha.2] - 2024-01-26
<a name="v3.5.1-rc.1"></a>
## [v3.5.1-rc.1] - 2024-01-23
<a name="v3.5.1-alpha.1"></a>
## [v3.5.1-alpha.1] - 2024-01-09
<a name="v3.5.0"></a>
## [v3.5.0] - 2024-01-02
<a name="v3.5.1"></a>
## [v3.5.1] - 2024-01-02
<a name="v3.5.1-bate.1"></a>
## [v3.5.1-bate.1] - 2024-01-02
<a name="v3.5.1-rc.0"></a>
## [v3.5.1-rc.0] - 2023-12-30
<a name="v3.5.0-rc.8"></a>
## [v3.5.0-rc.8] - 2023-12-28
<a name="v3.5.0-rc.7"></a>
## [v3.5.0-rc.7] - 2023-12-18
<a name="v3.5.0-rc.6"></a>
## [v3.5.0-rc.6] - 2023-12-15
<a name="v3.5.0-rc.5"></a>
## [v3.5.0-rc.5] - 2023-12-15
<a name="v3.5.0-rc.4"></a>
## [v3.5.0-rc.4] - 2023-12-14
<a name="v3.5.0-rc.3"></a>
## [v3.5.0-rc.3] - 2023-12-14
<a name="v3.5.0-rc.2"></a>
## [v3.5.0-rc.2] - 2023-12-14
<a name="v3.5.0-rc.1"></a>
## [v3.5.0-rc.1] - 2023-12-14
<a name="v3.5.0-rc.0"></a>
## [v3.5.0-rc.0] - 2023-12-14
<a name="v3.5.0-beta.1"></a>
## v3.5.0-beta.1 - 2023-11-29
### 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/v3.5.1-alpha.2...HEAD
[v3.5.1-alpha.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.1...v3.5.1-alpha.2
[v3.5.1-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...v3.5.1-rc.1
[v3.5.1-alpha.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0...v3.5.1-alpha.1
[v3.5.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.1...v3.5.0
[v3.5.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-bate.1...v3.5.1
[v3.5.1-bate.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.0...v3.5.1-bate.1
[v3.5.1-rc.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.8...v3.5.1-rc.0
[v3.5.0-rc.8]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.7...v3.5.0-rc.8
[v3.5.0-rc.7]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.6...v3.5.0-rc.7
[v3.5.0-rc.6]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.5...v3.5.0-rc.6
[v3.5.0-rc.5]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.4...v3.5.0-rc.5
[v3.5.0-rc.4]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.3...v3.5.0-rc.4
[v3.5.0-rc.3]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.2...v3.5.0-rc.3
[v3.5.0-rc.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.1...v3.5.0-rc.2
[v3.5.0-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.0...v3.5.0-rc.1
[v3.5.0-rc.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-beta.1...v3.5.0-rc.0

@ -1,20 +0,0 @@
# Version logging for OpenIM
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<a name="unreleased"></a>
## [Unreleased]
<a name="v3.6.0"></a>
## v3.6.0 - 2024-03-07
### 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/v3.6.0...HEAD

@ -1,173 +0,0 @@
# Changelog
- [Changelog](#changelog)
- [OpenIM versioning policy](#openim-versioning-policy)
- [command](#command)
- [install](#install)
- [User](#user)
- [create next tag](#create-next-tag)
- [Release version logs](#release-version-logs)
- [Introduction](#introduction)
- [Naming Format](#naming-format)
- [Examples](#examples)
- [Version Modifiers](#version-modifiers)
- [Versioning Strategy](#versioning-strategy)
All notable changes to this project will be documented in this file.
+ [https://github.com/openimsdk/open-im-server/releases](https://github.com/openimsdk/open-im-server/releases)
## OpenIM versioning policy
+ [OpenIM Version](../docs/contrib/version.md)
## command
To use git-chglog you need to configure:
1. CHANGELOG templates
2. git-chglog configuration
### install
```bash
$ go get github.com/git-chglog/git-chglog/cmd/git-chglog
```
## User
```bash
$ git-chglog --init
```
**Options**
- What is the URL of your repository?: https://github.com/openimsdk/open-im-server
- What is your favorite style?: github
- Choose the format of your favorite commit message: <type>(<scope>): <subject> -- feat(core): Add new feature
- What is your favorite template style?: standard
- Do you include Merge Commit in CHANGELOG?: n
- Do you include Revert Commit in CHANGELOG?: y
- In which directory do you output configuration files and templates?: .chglog
```bash
git-chglog --tag-filter-pattern 'v2.0.*' -o CHANGELOG-2.0.md
```
**Other uses:**
```bash
$ git-chglog
If <tag query> is not specified, it corresponds to all tags.
This is the simplest example.
$ git-chglog 1.0.0..2.0.0
The above is a command to generate CHANGELOG including commit of 1.0.0 to 2.0.0.
$ git-chglog 1.0.0
The above is a command to generate CHANGELOG including commit of only 1.0.0.
$ git-chglog $(git describe --tags $(git rev-list --tags --max-count=1))
The above is a command to generate CHANGELOG with the commit included in the latest tag.
$ git-chglog --output CHANGELOG.md
The above is a command to output to CHANGELOG.md instead of standard output.
$ git-chglog --config custom/dir/config.yml
The above is a command that uses a configuration file placed other than ".chglog/config.yml".
```
## create next tag
```bash
$ git-chglog --next-tag 2.0.0 -o CHANGELOG.md
$ git commit -am "release 2.0.0"
$ git tag 2.0.0
```
| Query | Description | Example |
| -------------- | ---------------------------------------------- | --------------------------- |
| `<old>..<new>` | Commit contained in `<new>` tags from `<old>`. | `$ git-chglog 1.0.0..2.0.0` |
| `<name>..` | Commit from the `<name>` to the latest tag. | `$ git-chglog 1.0.0..` |
| `..<name>` | Commit from the oldest tag to `<name>`. | `$ git-chglog ..2.0.0` |
| `<name>` | Commit contained in `<name>`. | `$ git-chglog 1.0.0` |
## Release version logs
+ [OpenIM CHANGELOG-V1.0](CHANGELOG-1.0.md)
+ [OpenIM CHANGELOG-V2.0](CHANGELOG-2.0.md)
+ [OpenIM CHANGELOG-V2.1](CHANGELOG-2.1.md)
+ [OpenIM CHANGELOG-V2.2](CHANGELOG-2.2.md)
+ [OpenIM CHANGELOG-V2.3](CHANGELOG-2.3.md)
+ [OpenIM CHANGELOG-V2.9](CHANGELOG-2.9.md)
+ [OpenIM CHANGELOG-V3.0](CHANGELOG-3.0.md)
+ [OpenIM CHANGELOG-V3.1](CHANGELOG-3.1.md)
+ [OpenIM CHANGELOG-V3.2](CHANGELOG-3.2.md)
+ [OpenIM CHANGELOG-V3.3](CHANGELOG-3.3.md)
## Introduction
In both the open-source and closed-source software development communities, it is important to follow a consistent and understandable versioning scheme for software projects. This ensures clear communication of changes, compatibility, and stability across different releases. One widely adopted naming convention is the Semantic Versioning 2.0.0.
## Naming Format
The most common format for version numbers is as follows:
```bash
major.minor[.patch[.build]]
```
Let's take a closer look at each component:
1. **Major Version**: This is the first number in the versioning scheme and indicates significant changes that may not be backward compatible (specific to each project).
2. **Minor Version**: The second number signifies the addition of new features while maintaining backward compatibility.
3. **Patch Version**: The third number represents bug fixes or code optimizations without introducing new features. It is generally backward compatible.
4. **Build Version**: Typically an automatically generated number that increments with each code commit.
## Examples
Here are a few examples to illustrate the versioning scheme:
1. `1.0`
2. `2.14.0.1478`
3. `3.2.1 build-354`
## Version Modifiers
Apart from the version numbers, there are also version modifiers used to indicate specific stages or statuses of a release. Some commonly used version modifiers include:
- **alpha**: An internal testing version with numerous known bugs. It is primarily used for communication among developers.
- **beta**: A testing version released to enthusiastic users for feedback and bug detection.
- **rc (release candidate)**: The final testing version before the official release.
- **ga (general availability)**: The initial stable release for public distribution.
- **r/release** (or no modifier at all): The final released version intended for general users.
- **lts (long-term support)**: Designates a version that will receive extended maintenance and bug fixes for a specified number of years.
## Versioning Strategy
To effectively manage version numbers, the following strategies are commonly employed:
- The initial version of a project can be either `0.1` or `1.0`.
- When fixing bugs, the patch version is incremented by 1.
- When adding new features, the minor version is incremented by 1, and the patch version is reset to 0.
- In the case of significant modifications, the major version is incremented by 1.
- The build version is usually automatically generated by the compilation process and follows a defined format. It does not require manual control.
By adhering to these strategies and guidelines, developers can maintain consistency and clarity in versioning their software projects. This enables users and collaborators to understand the nature of changes between different releases and ensure compatibility with their systems.
(Note: Markdown formatting has been used to structure this article. Markdown is a lightweight markup language used to format text on platforms like GitHub.)
------
**Note**: The above article is based on the given content and aims to provide a Markdown-formatted English article explaining the naming conventions for software project versions, specifically focusing on the Semantic Versioning 2.0.0.

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="./README.md">Englist</a> · <a href="./README.md">English</a> ·
<a href="./README_zh_CN.md">中文</a> · <a href="./README_zh_CN.md">中文</a> ·
<a href="./docs/readme/README_uk.md">Українська</a> · <a href="./docs/readme/README_uk.md">Українська</a> ·
<a href="./docs/readme/README_cs.md">Česky</a> · <a href="./docs/readme/README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="./README.md">Englist</a> · <a href="./README.md">English</a> ·
<a href="./README_zh_CN.md">中文</a> · <a href="./README_zh_CN.md">中文</a> ·
<a href="./docs/readme/README_uk.md">Українська</a> · <a href="./docs/readme/README_uk.md">Українська</a> ·
<a href="./docs/readme/README_cs.md">Česky</a> · <a href="./docs/readme/README_cs.md">Česky</a> ·

@ -1,8 +1,10 @@
{{ define "email.to.html" }} {{ define "email.to.html" }}
{{ range .Alerts }} {{ if eq .Status "firing" }}
<!-- Begin of OpenIM Alert --> {{ range .Alerts }}
<div style="border:1px solid #ccc; padding:10px; margin-bottom:10px;"> <!-- Begin of OpenIM Alert -->
<div style="border:1px solid #ccc; padding:10px; margin-bottom:10px;">
<h3>OpenIM Alert</h3> <h3>OpenIM Alert</h3>
<p><strong>Alert Status:</strong> firing</p>
<p><strong>Alert Program:</strong> Prometheus Alert</p> <p><strong>Alert Program:</strong> Prometheus Alert</p>
<p><strong>Severity Level:</strong> {{ .Labels.severity }}</p> <p><strong>Severity Level:</strong> {{ .Labels.severity }}</p>
<p><strong>Alert Type:</strong> {{ .Labels.alertname }}</p> <p><strong>Alert Type:</strong> {{ .Labels.alertname }}</p>
@ -10,7 +12,25 @@
<p><strong>Affected Service:</strong> {{ .Labels.job }}</p> <p><strong>Affected Service:</strong> {{ .Labels.job }}</p>
<p><strong>Alert Subject:</strong> {{ .Annotations.summary }}</p> <p><strong>Alert Subject:</strong> {{ .Annotations.summary }}</p>
<p><strong>Trigger Time:</strong> {{ .StartsAt.Format "2006-01-02 15:04:05" }}</p> <p><strong>Trigger Time:</strong> {{ .StartsAt.Format "2006-01-02 15:04:05" }}</p>
</div> </div>
{{ end }}
{{ else if eq .Status "resolved" }}
{{ range .Alerts }}
<!-- Begin of OpenIM Alert -->
<div style="border:1px solid #ccc; padding:10px; margin-bottom:10px;">
<h3>OpenIM Alert</h3>
<p><strong>Alert Status:</strong> resolved</p>
<p><strong>Alert Program:</strong> Prometheus Alert</p>
<p><strong>Severity Level:</strong> {{ .Labels.severity }}</p>
<p><strong>Alert Type:</strong> {{ .Labels.alertname }}</p>
<p><strong>Affected Host:</strong> {{ .Labels.instance }}</p>
<p><strong>Affected Service:</strong> {{ .Labels.job }}</p>
<p><strong>Alert Subject:</strong> {{ .Annotations.summary }}</p>
<p><strong>Trigger Time:</strong> {{ .StartsAt.Format "2006-01-02 15:04:05" }}</p>
</div>
{{ end }}
<!-- End of OpenIM Alert --> <!-- End of OpenIM Alert -->
{{ end }} {{ end }}
{{ end }} {{ end }}

@ -10,4 +10,5 @@ remainLogLevel: 6
isStdout: false isStdout: false
# Whether to log in JSON format, default is acceptable # Whether to log in JSON format, default is acceptable
isJson: false isJson: false
# output simplify log when KeyAndValues's value len is bigger than 50 in rpc method log
isSimplify: true

@ -129,7 +129,7 @@ REDIS_PORT=${REDIS_PORT}
# Default: REDIS_PASSWORD=openIM123 # Default: REDIS_PASSWORD=openIM123
REDIS_PASSWORD=${REDIS_PASSWORD} REDIS_PASSWORD=${REDIS_PASSWORD}
# Kakfa username to authenticate with the Kafka service. # Kafka username to authenticate with the Kafka service.
# KAFKA_USERNAME=${KAFKA_USERNAME} # KAFKA_USERNAME=${KAFKA_USERNAME}
# Port on which Kafka distributed streaming platform is running. # Port on which Kafka distributed streaming platform is running.

@ -32,7 +32,7 @@ This script offers a variety of utilities and helpers to enhance and simplify op
## brief descriptions of each function ## brief descriptions of each function
**Englist:** **English:**
1. `openim::util::ensure-gnu-sed` - Determines if GNU version of `sed` exists on the system and sets its name. 1. `openim::util::ensure-gnu-sed` - Determines if GNU version of `sed` exists on the system and sets its name.
2. `openim::util::ensure-gnu-date` - Determines if GNU version of `date` exists on the system and sets its name. 2. `openim::util::ensure-gnu-date` - Determines if GNU version of `date` exists on the system and sets its name.
3. `openim::util::check-file-in-alphabetical-order` - Checks if a file is sorted in alphabetical order. 3. `openim::util::check-file-in-alphabetical-order` - Checks if a file is sorted in alphabetical order.

@ -1,7 +1,7 @@
# How do I contribute code to OpenIM # How do I contribute code to OpenIM
<p align="center"> <p align="center">
<a href="./CONTRIBUTING.md">Englist</a> · <a href="./CONTRIBUTING.md">English</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> · <a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> · <a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> · <a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·

@ -1,7 +1,7 @@
# How do I contribute code to OpenIM # How do I contribute code to OpenIM
<p align="center"> <p align="center">
<a href="./CONTRIBUTING.md">Englist</a> · <a href="./CONTRIBUTING.md">English</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> · <a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> · <a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> · <a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md">Українська</a> · <a href="./README_uk.md">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -19,7 +19,7 @@
<p align="center"> <p align="center">
<a href="../../README.md">Englist</a> · <a href="../../README.md">English</a> ·
<a href="../../README_zh_CN.md">中文</a> · <a href="../../README_zh_CN.md">中文</a> ·
<a href="./README_uk.md ">Українська</a> · <a href="./README_uk.md ">Українська</a> ·
<a href="./README_cs.md">Česky</a> · <a href="./README_cs.md">Česky</a> ·

@ -12,8 +12,8 @@ require (
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/openimsdk/protocol v0.0.69-alpha.38 github.com/openimsdk/protocol v0.0.69-alpha.42
github.com/openimsdk/tools v0.0.49-alpha.51 github.com/openimsdk/tools v0.0.49-alpha.55
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_golang v1.18.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0

@ -319,10 +319,10 @@ github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/openimsdk/gomake v0.0.14-alpha.5 h1:VY9c5x515lTfmdhhPjMvR3BBRrRquAUCFsz7t7vbv7Y= github.com/openimsdk/gomake v0.0.14-alpha.5 h1:VY9c5x515lTfmdhhPjMvR3BBRrRquAUCFsz7t7vbv7Y=
github.com/openimsdk/gomake v0.0.14-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= github.com/openimsdk/gomake v0.0.14-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
github.com/openimsdk/protocol v0.0.69-alpha.38 h1:kVZCHIXg/el8YJFoIBWhZu1sbbTUqmzgF4l0W3sUH24= github.com/openimsdk/protocol v0.0.69-alpha.42 h1:Vwuru2NtyTHuqaM+1JGxcoGvP25QWjS92oI0zGJp+lM=
github.com/openimsdk/protocol v0.0.69-alpha.38/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8= github.com/openimsdk/protocol v0.0.69-alpha.42/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8=
github.com/openimsdk/tools v0.0.49-alpha.51 h1:JTPEetVSNOczw1n+XjiPozaH2SBPQAc+9VlPE41wEeY= github.com/openimsdk/tools v0.0.49-alpha.55 h1:KPgC53oqiwZYssLKljhtXbWXifMlTj2SSQEusj4Uf4k=
github.com/openimsdk/tools v0.0.49-alpha.51/go.mod h1:h1cYmfyaVtgFbKmb1Cfsl8XwUOMTt8ubVUQrdGtsUh4= github.com/openimsdk/tools v0.0.49-alpha.55/go.mod h1:h1cYmfyaVtgFbKmb1Cfsl8XwUOMTt8ubVUQrdGtsUh4=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=

@ -107,7 +107,6 @@ func (c *Client) pingHandler(appData string) error {
} }
log.ZDebug(c.ctx, "ping Handler Success.", "appData", appData) log.ZDebug(c.ctx, "ping Handler Success.", "appData", appData)
return c.writePongMsg(appData) return c.writePongMsg(appData)
} }
@ -392,17 +391,24 @@ func (c *Client) writePingMsg() error {
} }
func (c *Client) writePongMsg(appData string) error { func (c *Client) writePongMsg(appData string) error {
log.ZDebug(c.ctx, "write Pong Msg in Server", "appData", appData)
if c.closed.Load() { if c.closed.Load() {
return nil return nil
} }
log.ZDebug(c.ctx, "write Pong Msg in Server", "appData", appData)
c.w.Lock() c.w.Lock()
defer c.w.Unlock() defer c.w.Unlock()
log.ZDebug(c.ctx, "write Pong Msg in Server", "appData", appData)
err := c.conn.SetWriteDeadline(writeWait) err := c.conn.SetWriteDeadline(writeWait)
if err != nil { if err != nil {
return err return errs.Wrap(err)
}
err = c.conn.WriteMessage(PongMessage, []byte(appData))
if err != nil {
log.ZWarn(c.ctx, "Write Message have error", errs.Wrap(err), "Pong msg", PongMessage)
} }
return c.conn.WriteMessage(PongMessage, []byte(appData)) return errs.Wrap(err)
} }

@ -22,11 +22,15 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/msggateway" "github.com/openimsdk/protocol/msggateway"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mq/memamq"
"github.com/openimsdk/tools/utils/datautil"
"google.golang.org/grpc" "google.golang.org/grpc"
"sync/atomic"
) )
func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error { func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
@ -57,6 +61,7 @@ type Server struct {
pushTerminal map[int]struct{} pushTerminal map[int]struct{}
ready func(srv *Server) error ready func(srv *Server) error
userRcp rpcclient.UserRpcClient userRcp rpcclient.UserRpcClient
queue *memamq.MemoryQueue
} }
func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
@ -70,6 +75,7 @@ func NewServer(rpcPort int, longConnServer LongConnServer, conf *Config, ready f
pushTerminal: make(map[int]struct{}), pushTerminal: make(map[int]struct{}),
config: conf, config: conf,
ready: ready, ready: ready,
queue: memamq.NewMemoryQueue(512, 1024*16),
} }
s.pushTerminal[constant.IOSPlatformID] = struct{}{} s.pushTerminal[constant.IOSPlatformID] = struct{}{}
s.pushTerminal[constant.AndroidPlatformID] = struct{}{} s.pushTerminal[constant.AndroidPlatformID] = struct{}{}
@ -125,55 +131,93 @@ func (s *Server) OnlineBatchPushOneMsg(ctx context.Context, req *msggateway.Onli
return nil, nil return nil, nil
} }
func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq, func (s *Server) pushToUser(ctx context.Context, userID string, msgData *sdkws.MsgData) *msggateway.SingleMsgToUserResults {
) (*msggateway.OnlineBatchPushOneMsgResp, error) { clients, ok := s.LongConnServer.GetUserAllCons(userID)
var singleUserResults []*msggateway.SingleMsgToUserResults
for _, v := range req.PushToUserIDs {
var resp []*msggateway.SingleMsgToUserPlatform
results := &msggateway.SingleMsgToUserResults{
UserID: v,
}
clients, ok := s.LongConnServer.GetUserAllCons(v)
if !ok { if !ok {
log.ZDebug(ctx, "push user not online", "userID", v) log.ZDebug(ctx, "push user not online", "userID", userID)
results.Resp = resp return &msggateway.SingleMsgToUserResults{
singleUserResults = append(singleUserResults, results) UserID: userID,
continue }
}
log.ZDebug(ctx, "push user online", "clients", clients, "userID", userID)
result := &msggateway.SingleMsgToUserResults{
UserID: userID,
Resp: make([]*msggateway.SingleMsgToUserPlatform, 0, len(clients)),
} }
log.ZDebug(ctx, "push user online", "clients", clients, "userID", v)
for _, client := range clients { for _, client := range clients {
if client == nil { if client == nil {
continue continue
} }
userPlatform := &msggateway.SingleMsgToUserPlatform{ userPlatform := &msggateway.SingleMsgToUserPlatform{
RecvPlatFormID: int32(client.PlatformID), RecvPlatFormID: int32(client.PlatformID),
} }
if !client.IsBackground || if !client.IsBackground ||
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) { (client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
err := client.PushMessage(ctx, req.MsgData) err := client.PushMessage(ctx, msgData)
if err != nil { if err != nil {
userPlatform.ResultCode = int64(servererrs.ErrPushMsgErr.Code()) userPlatform.ResultCode = int64(servererrs.ErrPushMsgErr.Code())
resp = append(resp, userPlatform)
} else { } else {
if _, ok := s.pushTerminal[client.PlatformID]; ok { if _, ok := s.pushTerminal[client.PlatformID]; ok {
results.OnlinePush = true result.OnlinePush = true
resp = append(resp, userPlatform)
} }
} }
} else { } else {
userPlatform.ResultCode = int64(servererrs.ErrIOSBackgroundPushErr.Code()) userPlatform.ResultCode = int64(servererrs.ErrIOSBackgroundPushErr.Code())
resp = append(resp, userPlatform)
}
} }
results.Resp = resp result.Resp = append(result.Resp, userPlatform)
singleUserResults = append(singleUserResults, results)
} }
return result
}
return &msggateway.OnlineBatchPushOneMsgResp{ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) {
SinglePushResult: singleUserResults, if len(req.PushToUserIDs) == 0 {
}, nil return &msggateway.OnlineBatchPushOneMsgResp{}, nil
}
ch := make(chan *msggateway.SingleMsgToUserResults, len(req.PushToUserIDs))
var count atomic.Int64
count.Add(int64(len(req.PushToUserIDs)))
for i := range req.PushToUserIDs {
userID := req.PushToUserIDs[i]
err := s.queue.PushCtx(ctx, func() {
ch <- s.pushToUser(ctx, userID, req.MsgData)
if count.Add(-1) == 0 {
close(ch)
}
})
if err != nil {
if count.Add(-1) == 0 {
close(ch)
}
log.ZError(ctx, "pushToUser MemoryQueue failed", err, "userID", userID)
ch <- &msggateway.SingleMsgToUserResults{
UserID: userID,
}
}
}
resp := &msggateway.OnlineBatchPushOneMsgResp{
SinglePushResult: make([]*msggateway.SingleMsgToUserResults, 0, len(req.PushToUserIDs)),
}
for {
select {
case <-ctx.Done():
log.ZError(ctx, "SuperGroupOnlineBatchPushOneMsg ctx done", context.Cause(ctx))
userIDSet := datautil.SliceSet(req.PushToUserIDs)
for _, results := range resp.SinglePushResult {
delete(userIDSet, results.UserID)
}
for userID := range userIDSet {
resp.SinglePushResult = append(resp.SinglePushResult, &msggateway.SingleMsgToUserResults{
UserID: userID,
})
}
return resp, nil
case res, ok := <-ch:
if !ok {
return resp, nil
}
resp.SinglePushResult = append(resp.SinglePushResult, res)
}
}
} }
func (s *Server) KickUserOffline( func (s *Server) KickUserOffline(

@ -16,6 +16,7 @@ package friend
import ( import (
"context" "context"
"github.com/openimsdk/tools/mq/memamq"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
@ -49,6 +50,7 @@ type friendServer struct {
RegisterCenter discovery.SvcDiscoveryRegistry RegisterCenter discovery.SvcDiscoveryRegistry
config *Config config *Config
webhookClient *webhook.Client webhookClient *webhook.Client
queue *memamq.MemoryQueue
} }
type Config struct { type Config struct {
@ -118,8 +120,8 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
conversationRpcClient: rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation), conversationRpcClient: rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation),
config: config, config: config,
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL), webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
queue: memamq.NewMemoryQueue(128, 1024*8),
}) })
return nil return nil
} }

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/util/hashutil" "github.com/openimsdk/open-im-server/v3/pkg/util/hashutil"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
"slices" "slices"
"github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion" "github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion"
@ -17,13 +18,22 @@ func (s *friendServer) NotificationUserInfoUpdate(ctx context.Context, req *rela
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(userIDs) > 0 {
friendUserIDs := []string{req.UserID}
noCancelCtx := context.WithoutCancel(ctx)
err := s.queue.PushCtx(ctx, func() {
for _, userID := range userIDs { for _, userID := range userIDs {
if err := s.db.OwnerIncrVersion(ctx, userID, []string{req.UserID}, model.VersionStateUpdate); err != nil { if err := s.db.OwnerIncrVersion(noCancelCtx, userID, friendUserIDs, model.VersionStateUpdate); err != nil {
return nil, err log.ZError(ctx, "OwnerIncrVersion", err, "userID", userID, "friendUserIDs", friendUserIDs)
} }
} }
for _, userID := range userIDs { for _, userID := range userIDs {
s.notificationSender.FriendInfoUpdatedNotification(ctx, req.UserID, userID) s.notificationSender.FriendInfoUpdatedNotification(noCancelCtx, req.UserID, userID)
}
})
if err != nil {
log.ZError(ctx, "NotificationUserInfoUpdate timeout", err, "userID", req.UserID)
}
} }
return &relation.NotificationUserInfoUpdateResp{}, nil return &relation.NotificationUserInfoUpdateResp{}, nil
} }

@ -17,6 +17,7 @@ package group
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx"
@ -126,25 +127,8 @@ func (g *GroupNotificationSender) getGroupInfo(ctx context.Context, groupID stri
if len(ownerUserIDs) > 0 { if len(ownerUserIDs) > 0 {
ownerUserID = ownerUserIDs[0] ownerUserID = ownerUserIDs[0]
} }
return &sdkws.GroupInfo{
GroupID: gm.GroupID, return convert.Db2PbGroupInfo(gm, ownerUserID, num), nil
GroupName: gm.GroupName,
Notification: gm.Notification,
Introduction: gm.Introduction,
FaceURL: gm.FaceURL,
OwnerUserID: ownerUserID,
CreateTime: gm.CreateTime.UnixMilli(),
MemberCount: num,
Ex: gm.Ex,
Status: gm.Status,
CreatorUserID: gm.CreatorUserID,
GroupType: gm.GroupType,
NeedVerification: gm.NeedVerification,
LookMemberInfo: gm.LookMemberInfo,
ApplyMemberFriend: gm.ApplyMemberFriend,
NotificationUpdateTime: gm.NotificationUpdateTime.UnixMilli(),
NotificationUserID: gm.NotificationUserID,
}, nil
} }
func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
@ -198,29 +182,6 @@ func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Contex
return datautil.Slice(members, fn), nil return datautil.Slice(members, fn), nil
} }
//nolint:unused
func (g *GroupNotificationSender) groupDB2PB(group *model.Group, ownerUserID string, memberCount uint32) *sdkws.GroupInfo {
return &sdkws.GroupInfo{
GroupID: group.GroupID,
GroupName: group.GroupName,
Notification: group.Notification,
Introduction: group.Introduction,
FaceURL: group.FaceURL,
OwnerUserID: ownerUserID,
CreateTime: group.CreateTime.UnixMilli(),
MemberCount: memberCount,
Ex: group.Ex,
Status: group.Status,
CreatorUserID: group.CreatorUserID,
GroupType: group.GroupType,
NeedVerification: group.NeedVerification,
LookMemberInfo: group.LookMemberInfo,
ApplyMemberFriend: group.ApplyMemberFriend,
NotificationUpdateTime: group.NotificationUpdateTime.UnixMilli(),
NotificationUserID: group.NotificationUserID,
}
}
func (g *GroupNotificationSender) groupMemberDB2PB(member *model.GroupMember, appMangerLevel int32) *sdkws.GroupMemberFullInfo { func (g *GroupNotificationSender) groupMemberDB2PB(member *model.GroupMember, appMangerLevel int32) *sdkws.GroupMemberFullInfo {
return &sdkws.GroupMemberFullInfo{ return &sdkws.GroupMemberFullInfo{
GroupID: member.GroupID, GroupID: member.GroupID,

@ -111,7 +111,7 @@ func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sd
func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (resp *msg.SearchMessageResp, err error) { func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (resp *msg.SearchMessageResp, err error) {
var chatLogs []*sdkws.MsgData var chatLogs []*sdkws.MsgData
var total int32 var total int64
resp = &msg.SearchMessageResp{} resp = &msg.SearchMessageResp{}
if total, chatLogs, err = m.MsgDatabase.SearchMessage(ctx, req); err != nil { if total, chatLogs, err = m.MsgDatabase.SearchMessage(ctx, req); err != nil {
return nil, err return nil, err
@ -194,7 +194,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
} }
resp.ChatLogs = append(resp.ChatLogs, pbchatLog) resp.ChatLogs = append(resp.ChatLogs, pbchatLog)
} }
resp.ChatLogsNum = total resp.ChatLogsNum = int32(total)
return resp, nil return resp, nil
} }

@ -50,13 +50,14 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq)
platform := constant.PlatformID2Name[int(req.Platform)] platform := constant.PlatformID2Name[int(req.Platform)]
for _, fileURL := range req.FileURLs { for _, fileURL := range req.FileURLs {
log := relationtb.Log{ log := relationtb.Log{
Version: req.Version,
SystemType: req.SystemType,
Platform: platform, Platform: platform,
UserID: userID, UserID: userID,
CreateTime: time.Now(), CreateTime: time.Now(),
Url: fileURL.URL, Url: fileURL.URL,
FileName: fileURL.Filename, FileName: fileURL.Filename,
SystemType: req.SystemType,
Version: req.Version,
Ex: req.Ex,
} }
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
id := genLogID() id := genLogID()

@ -139,6 +139,7 @@ func (r *RootCmd) initializeLogger(cmdOpts *CmdOpts) error {
r.log.RemainRotationCount, r.log.RemainRotationCount,
r.log.RotationTime, r.log.RotationTime,
config.Version, config.Version,
r.log.IsSimplify,
) )
if err != nil { if err != nil {
return errs.Wrap(err) return errs.Wrap(err)

@ -15,6 +15,9 @@
package config package config
import ( import (
"strings"
"time"
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/tools/mq/kafka" "github.com/openimsdk/tools/mq/kafka"
@ -22,8 +25,6 @@ import (
"github.com/openimsdk/tools/s3/kodo" "github.com/openimsdk/tools/s3/kodo"
"github.com/openimsdk/tools/s3/minio" "github.com/openimsdk/tools/s3/minio"
"github.com/openimsdk/tools/s3/oss" "github.com/openimsdk/tools/s3/oss"
"strings"
"time"
) )
type CacheConfig struct { type CacheConfig struct {
@ -48,6 +49,7 @@ type Log struct {
RemainLogLevel int `mapstructure:"remainLogLevel"` RemainLogLevel int `mapstructure:"remainLogLevel"`
IsStdout bool `mapstructure:"isStdout"` IsStdout bool `mapstructure:"isStdout"`
IsJson bool `mapstructure:"isJson"` IsJson bool `mapstructure:"isJson"`
IsSimplify bool `mapstructure:"isSimplify"`
WithStack bool `mapstructure:"withStack"` WithStack bool `mapstructure:"withStack"`
} }

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/dtm-labs/rockscache" "github.com/dtm-labs/rockscache"
"github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"golang.org/x/sync/singleflight" "golang.org/x/sync/singleflight"
"time" "time"
@ -49,6 +50,7 @@ func batchGetCache2[K comparable, V any](ctx context.Context, rcClient *rockscac
} }
values, err := fn(ctx, queryIds) values, err := fn(ctx, queryIds)
if err != nil { if err != nil {
log.ZError(ctx, "batchGetCache query database failed", err, "keys", keys, "queryIds", queryIds)
return nil, err return nil, err
} }
if len(values) == 0 { if len(values) == 0 {

@ -118,6 +118,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin
v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) { v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) {
t, err = fn(ctx) t, err = fn(ctx)
if err != nil { if err != nil {
log.ZError(ctx, "getCache query database failed", err, "key", key)
return "", err return "", err
} }
bs, err := json.Marshal(t) bs, err := json.Marshal(t)

@ -45,7 +45,7 @@ func TestName(t *testing.T) {
} }
seqUser := NewSeqUserCacheRedis(rdb, mgoSeqUser) seqUser := NewSeqUserCacheRedis(rdb, mgoSeqUser)
res, err := seqUser.GetReadSeqs(ctx, "2110910952", []string{"sg_2920732023", "sg_345762580"}) res, err := seqUser.GetUserReadSeqs(ctx, "2110910952", []string{"sg_2920732023", "sg_345762580"})
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -77,8 +77,8 @@ func (s *seqConversationCacheRedis) batchGetMaxSeq(ctx context.Context, keys []s
return errs.Wrap(err) return errs.Wrap(err)
} }
} }
if len(notFoundKey) > 0 { for _, key := range notFoundKey {
conversationID := keyConversationID[notFoundKey[0]] conversationID := keyConversationID[key]
seq, err := s.GetMaxSeq(ctx, conversationID) seq, err := s.GetMaxSeq(ctx, conversationID)
if err != nil { if err != nil {
return err return err

@ -44,38 +44,38 @@ func (s *seqUserCacheRedis) getSeqUserReadSeqKey(conversationID string, userID s
return cachekey.GetSeqUserReadSeqKey(conversationID, userID) return cachekey.GetSeqUserReadSeqKey(conversationID, userID)
} }
func (s *seqUserCacheRedis) GetMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserCacheRedis) GetUserMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return getCache(ctx, s.rocks, s.getSeqUserMaxSeqKey(conversationID, userID), s.expireTime, func(ctx context.Context) (int64, error) { return getCache(ctx, s.rocks, s.getSeqUserMaxSeqKey(conversationID, userID), s.expireTime, func(ctx context.Context) (int64, error) {
return s.mgo.GetMaxSeq(ctx, conversationID, userID) return s.mgo.GetUserMaxSeq(ctx, conversationID, userID)
}) })
} }
func (s *seqUserCacheRedis) SetMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserCacheRedis) SetUserMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
if err := s.mgo.SetMaxSeq(ctx, conversationID, userID, seq); err != nil { if err := s.mgo.SetUserMaxSeq(ctx, conversationID, userID, seq); err != nil {
return err return err
} }
return s.rocks.TagAsDeleted2(ctx, s.getSeqUserMaxSeqKey(conversationID, userID)) return s.rocks.TagAsDeleted2(ctx, s.getSeqUserMaxSeqKey(conversationID, userID))
} }
func (s *seqUserCacheRedis) GetMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserCacheRedis) GetUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return getCache(ctx, s.rocks, s.getSeqUserMinSeqKey(conversationID, userID), s.expireTime, func(ctx context.Context) (int64, error) { return getCache(ctx, s.rocks, s.getSeqUserMinSeqKey(conversationID, userID), s.expireTime, func(ctx context.Context) (int64, error) {
return s.mgo.GetMaxSeq(ctx, conversationID, userID) return s.mgo.GetUserMinSeq(ctx, conversationID, userID)
}) })
} }
func (s *seqUserCacheRedis) SetMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserCacheRedis) SetUserMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
return s.SetMinSeqs(ctx, userID, map[string]int64{conversationID: seq}) return s.SetUserMinSeqs(ctx, userID, map[string]int64{conversationID: seq})
} }
func (s *seqUserCacheRedis) GetReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserCacheRedis) GetUserReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return getCache(ctx, s.rocks, s.getSeqUserReadSeqKey(conversationID, userID), s.readExpireTime, func(ctx context.Context) (int64, error) { return getCache(ctx, s.rocks, s.getSeqUserReadSeqKey(conversationID, userID), s.readExpireTime, func(ctx context.Context) (int64, error) {
return s.mgo.GetMaxSeq(ctx, conversationID, userID) return s.mgo.GetUserReadSeq(ctx, conversationID, userID)
}) })
} }
func (s *seqUserCacheRedis) SetReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserCacheRedis) SetUserReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
if seq%s.readSeqWriteRatio == 0 { if seq%s.readSeqWriteRatio == 0 {
if err := s.mgo.SetReadSeq(ctx, conversationID, userID, seq); err != nil { if err := s.mgo.SetUserReadSeq(ctx, conversationID, userID, seq); err != nil {
return err return err
} }
} }
@ -85,10 +85,10 @@ func (s *seqUserCacheRedis) SetReadSeq(ctx context.Context, conversationID strin
return nil return nil
} }
func (s *seqUserCacheRedis) SetMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error { func (s *seqUserCacheRedis) SetUserMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error {
keys := make([]string, 0, len(seqs)) keys := make([]string, 0, len(seqs))
for conversationID, seq := range seqs { for conversationID, seq := range seqs {
if err := s.mgo.SetMinSeq(ctx, conversationID, userID, seq); err != nil { if err := s.mgo.SetUserMinSeq(ctx, conversationID, userID, seq); err != nil {
return err return err
} }
keys = append(keys, s.getSeqUserMinSeqKey(conversationID, userID)) keys = append(keys, s.getSeqUserMinSeqKey(conversationID, userID))
@ -96,7 +96,7 @@ func (s *seqUserCacheRedis) SetMinSeqs(ctx context.Context, userID string, seqs
return DeleteCacheBySlot(ctx, s.rocks, keys) return DeleteCacheBySlot(ctx, s.rocks, keys)
} }
func (s *seqUserCacheRedis) setRedisReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error { func (s *seqUserCacheRedis) setUserRedisReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error {
keys := make([]string, 0, len(seqs)) keys := make([]string, 0, len(seqs))
keySeq := make(map[string]int64) keySeq := make(map[string]int64)
for conversationID, seq := range seqs { for conversationID, seq := range seqs {
@ -121,16 +121,16 @@ func (s *seqUserCacheRedis) setRedisReadSeqs(ctx context.Context, userID string,
return nil return nil
} }
func (s *seqUserCacheRedis) SetReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error { func (s *seqUserCacheRedis) SetUserReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error {
if len(seqs) == 0 { if len(seqs) == 0 {
return nil return nil
} }
if err := s.setRedisReadSeqs(ctx, userID, seqs); err != nil { if err := s.setUserRedisReadSeqs(ctx, userID, seqs); err != nil {
return err return err
} }
for conversationID, seq := range seqs { for conversationID, seq := range seqs {
if seq%s.readSeqWriteRatio == 0 { if seq%s.readSeqWriteRatio == 0 {
if err := s.mgo.SetReadSeq(ctx, conversationID, userID, seq); err != nil { if err := s.mgo.SetUserReadSeq(ctx, conversationID, userID, seq); err != nil {
return err return err
} }
} }
@ -138,13 +138,13 @@ func (s *seqUserCacheRedis) SetReadSeqs(ctx context.Context, userID string, seqs
return nil return nil
} }
func (s *seqUserCacheRedis) GetReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { func (s *seqUserCacheRedis) GetUserReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
res, err := batchGetCache2(ctx, s.rocks, s.readExpireTime, conversationIDs, func(conversationID string) string { res, err := batchGetCache2(ctx, s.rocks, s.readExpireTime, conversationIDs, func(conversationID string) string {
return s.getSeqUserReadSeqKey(conversationID, userID) return s.getSeqUserReadSeqKey(conversationID, userID)
}, func(v *readSeqModel) string { }, func(v *readSeqModel) string {
return v.ConversationID return v.ConversationID
}, func(ctx context.Context, conversationIDs []string) ([]*readSeqModel, error) { }, func(ctx context.Context, conversationIDs []string) ([]*readSeqModel, error) {
seqs, err := s.mgo.GetReadSeqs(ctx, userID, conversationIDs) seqs, err := s.mgo.GetUserReadSeqs(ctx, userID, conversationIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -4,7 +4,10 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/cachekey"
mgo2 "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log" "log"
"strconv" "strconv"
"sync/atomic" "sync/atomic"
@ -77,3 +80,32 @@ func TestRecvOnline(t *testing.T) {
fmt.Printf("Received message from channel %s: %s\n", msg.Channel, msg.Payload) fmt.Printf("Received message from channel %s: %s\n", msg.Channel, msg.Payload)
} }
} }
func TestName1(t *testing.T) {
opt := &redis.Options{
Addr: "172.16.8.48:16379",
Password: "openIM123",
DB: 0,
}
rdb := redis.NewClient(opt)
mgo, err := mongo.Connect(context.Background(),
options.Client().
ApplyURI("mongodb://openIM:openIM123@172.16.8.48:37017/openim_v3?maxPoolSize=100").
SetConnectTimeout(5*time.Second))
if err != nil {
panic(err)
}
model, err := mgo2.NewSeqUserMongo(mgo.Database("openim_v3"))
if err != nil {
panic(err)
}
seq := NewSeqUserCacheRedis(rdb, model)
res, err := seq.GetUserReadSeqs(context.Background(), "2110910952", []string{"sg_345762580", "2000", "3000"})
if err != nil {
panic(err)
}
t.Log(res)
}

@ -3,13 +3,13 @@ package cache
import "context" import "context"
type SeqUser interface { type SeqUser interface {
GetMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error
GetMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error
GetReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserReadSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error
SetMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error SetUserMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error
SetReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error SetUserReadSeqs(ctx context.Context, userID string, seqs map[string]int64) error
GetReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) GetUserReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error)
} }

@ -84,7 +84,7 @@ type CommonMsgDatabase interface {
//GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) //GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error)
SetSendMsgStatus(ctx context.Context, id string, status int32) error SetSendMsgStatus(ctx context.Context, id string, status int32) error
GetSendMsgStatus(ctx context.Context, id string) (int32, error) GetSendMsgStatus(ctx context.Context, id string) (int32, error)
SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err error) SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int64, msgData []*sdkws.MsgData, err error)
FindOneByDocIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) FindOneByDocIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error)
// to mq // to mq
@ -334,7 +334,7 @@ func (db *commonMsgDatabase) DeleteMessagesFromCache(ctx context.Context, conver
func (db *commonMsgDatabase) setHasReadSeqs(ctx context.Context, conversationID string, userSeqMap map[string]int64) error { func (db *commonMsgDatabase) setHasReadSeqs(ctx context.Context, conversationID string, userSeqMap map[string]int64) error {
for userID, seq := range userSeqMap { for userID, seq := range userSeqMap {
if err := db.seqUser.SetReadSeq(ctx, conversationID, userID, seq); err != nil { if err := db.seqUser.SetUserReadSeq(ctx, conversationID, userID, seq); err != nil {
return err return err
} }
} }
@ -498,7 +498,7 @@ func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID strin
// "userMinSeq" can be set as the same value as the conversation's "maxSeq" at the moment they join the group. // "userMinSeq" can be set as the same value as the conversation's "maxSeq" at the moment they join the group.
// This ensures that their message retrieval starts from the point they joined. // This ensures that their message retrieval starts from the point they joined.
func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (int64, int64, []*sdkws.MsgData, error) { func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (int64, int64, []*sdkws.MsgData, error) {
userMinSeq, err := db.seqUser.GetMinSeq(ctx, conversationID, userID) userMinSeq, err := db.seqUser.GetUserMinSeq(ctx, conversationID, userID)
if err != nil && errs.Unwrap(err) != redis.Nil { if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err return 0, 0, nil, err
} }
@ -576,7 +576,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
} }
func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (int64, int64, []*sdkws.MsgData, error) { func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (int64, int64, []*sdkws.MsgData, error) {
userMinSeq, err := db.seqUser.GetMinSeq(ctx, conversationID, userID) userMinSeq, err := db.seqUser.GetUserMinSeq(ctx, conversationID, userID)
if err != nil && errs.Unwrap(err) != redis.Nil { if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err return 0, 0, nil, err
} }
@ -674,12 +674,12 @@ func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string
log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversationID, "userID", userID, "seqs", seqs) log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversationID, "userID", userID, "seqs", seqs)
if len(seqs) > 0 { if len(seqs) > 0 {
userMinSeq := seqs[len(seqs)-1] + 1 userMinSeq := seqs[len(seqs)-1] + 1
currentUserMinSeq, err := db.seqUser.GetMinSeq(ctx, conversationID, userID) currentUserMinSeq, err := db.seqUser.GetUserMinSeq(ctx, conversationID, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if currentUserMinSeq < userMinSeq { if currentUserMinSeq < userMinSeq {
if err := db.seqUser.SetMinSeq(ctx, conversationID, userID, userMinSeq); err != nil { if err := db.seqUser.SetUserMinSeq(ctx, conversationID, userID, userMinSeq); err != nil {
return nil, err return nil, err
} }
} }
@ -721,7 +721,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
} }
log.ZDebug(ctx, "doc info", "conversationID", conversationID, "index", index, "docID", msgDocModel.DocID, "len", len(msgDocModel.Msg)) log.ZDebug(ctx, "doc info", "conversationID", conversationID, "index", index, "docID", msgDocModel.DocID, "len", len(msgDocModel.Msg))
if int64(len(msgDocModel.Msg)) > db.msgTable.GetSingleGocMsgNum() { if int64(len(msgDocModel.Msg)) > db.msgTable.GetSingleGocMsgNum() {
log.ZWarn(ctx, "msgs too large", nil, "lenth", len(msgDocModel.Msg), "docID:", msgDocModel.DocID) log.ZWarn(ctx, "msgs too large", nil, "length", len(msgDocModel.Msg), "docID:", msgDocModel.DocID)
} }
if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < timeutil.GetCurrentTimestampByMill() { if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < timeutil.GetCurrentTimestampByMill() {
log.ZDebug(ctx, "doc is full and all msg is expired", "docID", msgDocModel.DocID) log.ZDebug(ctx, "doc is full and all msg is expired", "docID", msgDocModel.DocID)
@ -794,23 +794,23 @@ func (db *commonMsgDatabase) SetMinSeqs(ctx context.Context, seqs map[string]int
} }
func (db *commonMsgDatabase) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error { func (db *commonMsgDatabase) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error {
return db.seqUser.SetMinSeqs(ctx, userID, seqs) return db.seqUser.SetUserMinSeqs(ctx, userID, seqs)
} }
func (db *commonMsgDatabase) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error { func (db *commonMsgDatabase) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error {
return db.seqUser.SetReadSeqs(ctx, userID, hasReadSeqs) return db.seqUser.SetUserReadSeqs(ctx, userID, hasReadSeqs)
} }
func (db *commonMsgDatabase) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error { func (db *commonMsgDatabase) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error {
return db.seqUser.SetReadSeq(ctx, conversationID, userID, hasReadSeq) return db.seqUser.SetUserReadSeq(ctx, conversationID, userID, hasReadSeq)
} }
func (db *commonMsgDatabase) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { func (db *commonMsgDatabase) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
return db.seqUser.GetReadSeqs(ctx, userID, conversationIDs) return db.seqUser.GetUserReadSeqs(ctx, userID, conversationIDs)
} }
func (db *commonMsgDatabase) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) { func (db *commonMsgDatabase) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) {
return db.seqUser.GetReadSeq(ctx, conversationID, userID) return db.seqUser.GetUserReadSeq(ctx, conversationID, userID)
} }
func (db *commonMsgDatabase) SetSendMsgStatus(ctx context.Context, id string, status int32) error { func (db *commonMsgDatabase) SetSendMsgStatus(ctx context.Context, id string, status int32) error {
@ -878,7 +878,7 @@ func (db *commonMsgDatabase) RangeGroupSendCount(
return db.msgDocDatabase.RangeGroupSendCount(ctx, start, end, ase, pageNumber, showNumber) return db.msgDocDatabase.RangeGroupSendCount(ctx, start, end, ase, pageNumber, showNumber)
} }
func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err error) { func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int64, msgData []*sdkws.MsgData, err error) {
var totalMsgs []*sdkws.MsgData var totalMsgs []*sdkws.MsgData
total, msgs, err := db.msgDocDatabase.SearchMessage(ctx, req) total, msgs, err := db.msgDocDatabase.SearchMessage(ctx, req)
if err != nil { if err != nil {

@ -110,6 +110,7 @@ func (u *userDatabase) InitOnce(ctx context.Context, users []*model.User) error
// FindWithError Get the information of the specified user and return an error if the userID is not found. // FindWithError Get the information of the specified user and return an error if the userID is not found.
func (u *userDatabase) FindWithError(ctx context.Context, userIDs []string) (users []*model.User, err error) { func (u *userDatabase) FindWithError(ctx context.Context, userIDs []string) (users []*model.User, err error) {
userIDs = datautil.Distinct(userIDs)
users, err = u.cache.GetUsersInfo(ctx, userIDs) users, err = u.cache.GetUsersInfo(ctx, userIDs)
if err != nil { if err != nil {
return return

@ -94,6 +94,29 @@ func (m *MsgMgo) FindOneByDocID(ctx context.Context, docID string) (*model.MsgDo
} }
func (m *MsgMgo) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID, docID string, seqs []int64) ([]*model.MsgInfoModel, error) { func (m *MsgMgo) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID, docID string, seqs []int64) ([]*model.MsgInfoModel, error) {
msgs, err := m.getMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs)
if err != nil {
return nil, err
}
if len(msgs) == len(seqs) {
return msgs, nil
}
tmp := make(map[int64]*model.MsgInfoModel)
for i, val := range msgs {
tmp[val.Msg.Seq] = msgs[i]
}
res := make([]*model.MsgInfoModel, 0, len(seqs))
for _, seq := range seqs {
if val, ok := tmp[seq]; ok {
res = append(res, val)
} else {
res = append(res, &model.MsgInfoModel{Msg: &model.MsgDataModel{Seq: seq}})
}
}
return res, nil
}
func (m *MsgMgo) getMsgBySeqIndexIn1Doc(ctx context.Context, userID, docID string, seqs []int64) ([]*model.MsgInfoModel, error) {
indexs := make([]int64, 0, len(seqs)) indexs := make([]int64, 0, len(seqs))
for _, seq := range seqs { for _, seq := range seqs {
indexs = append(indexs, m.model.GetMsgIndex(seq)) indexs = append(indexs, m.model.GetMsgIndex(seq))
@ -255,124 +278,409 @@ func (m *MsgMgo) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, do
return nil return nil
} }
func (m *MsgMgo) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*model.MsgInfoModel, error) { //func (m *MsgMgo) searchCount(ctx context.Context, filter any) (int64, error) {
where := make(bson.A, 0, 6) //
// return nil, nil
//}
//func (m *MsgMgo) searchMessage(ctx context.Context, filter any, nextID primitive.ObjectID, content bool, limit int) (int64, []*model.MsgInfoModel, primitive.ObjectID, error) {
// var pipeline bson.A
// if !nextID.IsZero() {
// pipeline = append(pipeline, bson.M{"$match": bson.M{"_id": bson.M{"$gt": nextID}}})
// }
// pipeline = append(pipeline,
// bson.M{"$match": filter},
// bson.M{"$limit": limit},
// bson.M{"$unwind": "$msgs"},
// bson.M{"$match": filter},
// bson.M{
// "$group": bson.M{
// "_id": "$_id",
// "doc_id": bson.M{
// "$first": "$doc_id",
// },
// "msgs": bson.M{"$push": "$msgs"},
// },
// },
// )
// if !content {
// pipeline = append(pipeline,
// bson.M{
// "$project": bson.M{
// "_id": 1,
// "count": bson.M{"$size": "$msgs"},
// },
// },
// )
// type result struct {
// ID primitive.ObjectID `bson:"_id"`
// Count int64 `bson:"count"`
// }
// res, err := mongoutil.Aggregate[result](ctx, m.coll, pipeline)
// if err != nil {
// return 0, nil, primitive.ObjectID{}, err
// }
// if len(res) == 0 {
// return 0, nil, primitive.ObjectID{}, nil
// }
// var count int64
// for _, r := range res {
// count += r.Count
// }
// return count, nil, res[len(res)-1].ID, nil
// }
// type result struct {
// ID primitive.ObjectID `bson:"_id"`
// Msg []*model.MsgInfoModel `bson:"msgs"`
// }
// res, err := mongoutil.Aggregate[result](ctx, m.coll, pipeline)
// if err != nil {
// return 0, nil, primitive.ObjectID{}, err
// }
// if len(res) == 0 {
// return 0, nil, primitive.ObjectID{}, err
// }
// var count int
// for _, r := range res {
// count += len(r.Msg)
// }
// msgs := make([]*model.MsgInfoModel, 0, count)
// for _, r := range res {
// msgs = append(msgs, r.Msg...)
// }
// return int64(count), msgs, res[len(res)-1].ID, nil
//}
/*
db.msg3.aggregate(
[
{
"$match": {
"doc_id": "si_7009965934_8710838466:0"
},
}
]
)
*/
type searchMessageIndex struct {
ID primitive.ObjectID `bson:"_id"`
Index []int64 `bson:"index"`
}
func (m *MsgMgo) searchMessageIndex(ctx context.Context, filter any, nextID primitive.ObjectID, limit int) ([]searchMessageIndex, error) {
var pipeline bson.A
if !nextID.IsZero() {
pipeline = append(pipeline, bson.M{"$match": bson.M{"_id": bson.M{"$gt": nextID}}})
}
pipeline = append(pipeline,
bson.M{"$sort": bson.M{"_id": 1}},
bson.M{"$match": filter},
bson.M{"$limit": limit},
bson.M{
"$project": bson.M{
"_id": 1,
"msgs": bson.M{
"$map": bson.M{
"input": "$msgs",
"as": "msg",
"in": bson.M{
"$mergeObjects": bson.A{
"$$msg",
bson.M{
"_search_temp_index": bson.M{
"$indexOfArray": bson.A{
"$msgs", "$$msg",
},
},
},
},
},
},
},
},
},
bson.M{"$unwind": "$msgs"},
bson.M{"$match": filter},
bson.M{
"$project": bson.M{
"_id": 1,
"msgs._search_temp_index": 1,
},
},
bson.M{
"$group": bson.M{
"_id": "$_id",
"index": bson.M{"$push": "$msgs._search_temp_index"},
},
},
bson.M{"$sort": bson.M{"_id": 1}},
)
return mongoutil.Aggregate[searchMessageIndex](ctx, m.coll, pipeline)
}
func (m *MsgMgo) searchMessage(ctx context.Context, req *msg.SearchMessageReq) (int64, []searchMessageIndex, error) {
filter := bson.M{}
if req.RecvID != "" { if req.RecvID != "" {
where = append(where, bson.M{"msgs.msg.recv_id": req.RecvID}) filter["$or"] = bson.A{
bson.M{"msgs.msg.recv_id": req.RecvID},
bson.M{"msgs.msg.group_id": req.RecvID},
}
} }
if req.SendID != "" { if req.SendID != "" {
where = append(where, bson.M{"msgs.msg.send_id": req.SendID}) filter["msgs.msg.send_id"] = req.SendID
} }
if req.ContentType != 0 { if req.ContentType != 0 {
where = append(where, bson.M{"msgs.msg.content_type": req.ContentType}) filter["msgs.msg.content_type"] = req.ContentType
} }
if req.SessionType != 0 { if req.SessionType != 0 {
where = append(where, bson.M{"msgs.msg.session_type": req.SessionType}) filter["msgs.msg.session_type"] = req.SessionType
} }
if req.SendTime != "" { if req.SendTime != "" {
sendTime, err := time.Parse(time.DateOnly, req.SendTime) sendTime, err := time.Parse(time.DateOnly, req.SendTime)
if err != nil { if err != nil {
return 0, nil, errs.ErrArgs.WrapMsg("invalid sendTime", "req", req.SendTime, "format", time.DateOnly, "cause", err.Error()) return 0, nil, errs.ErrArgs.WrapMsg("invalid sendTime", "req", req.SendTime, "format", time.DateOnly, "cause", err.Error())
} }
where = append(where, filter["$and"] = bson.A{
bson.M{ bson.M{"msgs.msg.send_time": bson.M{
"msgs.msg.send_time": bson.M{
"$gte": sendTime.UnixMilli(), "$gte": sendTime.UnixMilli(),
}, }},
},
bson.M{ bson.M{
"msgs.msg.send_time": bson.M{ "msgs.msg.send_time": bson.M{
"$lt": sendTime.Add(time.Hour * 24).UnixMilli(), "$lt": sendTime.Add(time.Hour * 24).UnixMilli(),
}, },
}, },
}
}
var (
nextID primitive.ObjectID
count int
dataRange []searchMessageIndex
skip = int((req.Pagination.GetPageNumber() - 1) * req.Pagination.GetShowNumber())
) )
_, _ = dataRange, skip
const maxDoc = 50
data := make([]searchMessageIndex, 0, req.Pagination.GetShowNumber())
push := cap(data)
for i := 0; ; i++ {
res, err := m.searchMessageIndex(ctx, filter, nextID, maxDoc)
if err != nil {
return 0, nil, err
} }
pipeline := bson.A{ if len(res) > 0 {
bson.M{ nextID = res[len(res)-1].ID
"$unwind": "$msgs", }
}, for _, r := range res {
var dataIndex []int64
for _, index := range r.Index {
if push > 0 && count >= skip {
dataIndex = append(dataIndex, index)
push--
}
count++
} }
if len(where) > 0 { if len(dataIndex) > 0 {
pipeline = append(pipeline, bson.M{ data = append(data, searchMessageIndex{
"$match": bson.M{"$and": where}, ID: r.ID,
Index: dataIndex,
}) })
} }
pipeline = append(pipeline, }
bson.M{ if push <= 0 {
"$project": bson.M{ push--
"_id": 0, }
"msg": "$msgs.msg", if len(res) < maxDoc || push < -10 {
}, return int64(count), data, nil
}, }
bson.M{ }
"$count": "count", }
},
) func (m *MsgMgo) getDocRange(ctx context.Context, id primitive.ObjectID, index []int64) ([]*model.MsgInfoModel, error) {
count, err := mongoutil.Aggregate[int32](ctx, m.coll, pipeline) if len(index) == 0 {
return nil, nil
}
pipeline := bson.A{
bson.M{"$match": bson.M{"_id": id}},
bson.M{"$project": "$msgs"},
}
msgs, err := mongoutil.Aggregate[*model.MsgInfoModel](ctx, m.coll, pipeline)
if err != nil {
return nil, err
}
return msgs, nil
}
func (m *MsgMgo) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int64, []*model.MsgInfoModel, error) {
count, data, err := m.searchMessage(ctx, req)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
if len(count) == 0 || count[0] == 0 { var msgs []*model.MsgInfoModel
return 0, nil, nil if len(data) > 0 {
var n int
for _, d := range data {
n += len(d.Index)
} }
pipeline = pipeline[:len(pipeline)-1] msgs = make([]*model.MsgInfoModel, 0, n)
pipeline = append(pipeline, }
bson.M{ for _, val := range data {
"$skip": (req.Pagination.GetPageNumber() - 1) * req.Pagination.GetShowNumber(), res, err := mongoutil.FindOne[*model.MsgDocModel](ctx, m.coll, bson.M{"_id": val.ID})
},
bson.M{
"$limit": req.Pagination.GetShowNumber(),
},
)
msgs, err := mongoutil.Aggregate[*model.MsgInfoModel](ctx, m.coll, pipeline)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
for i := range msgs { for _, i := range val.Index {
msgInfo := msgs[i] if i >= int64(len(res.Msg)) {
if msgInfo == nil || msgInfo.Msg == nil {
continue continue
} }
if msgInfo.Revoke != nil { msgs = append(msgs, res.Msg[i])
revokeContent := sdkws.MessageRevokedContent{
RevokerID: msgInfo.Revoke.UserID,
RevokerRole: msgInfo.Revoke.Role,
ClientMsgID: msgInfo.Msg.ClientMsgID,
RevokerNickname: msgInfo.Revoke.Nickname,
RevokeTime: msgInfo.Revoke.Time,
SourceMessageSendTime: msgInfo.Msg.SendTime,
SourceMessageSendID: msgInfo.Msg.SendID,
SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
SessionType: msgInfo.Msg.SessionType,
Seq: msgInfo.Msg.Seq,
Ex: msgInfo.Msg.Ex,
} }
data, err := jsonutil.JsonMarshal(&revokeContent)
if err != nil {
return 0, nil, errs.WrapMsg(err, "json.Marshal revokeContent")
} }
elem := sdkws.NotificationElem{Detail: string(data)} return count, msgs, nil
content, err := jsonutil.JsonMarshal(&elem)
if err != nil {
return 0, nil, errs.WrapMsg(err, "json.Marshal elem")
}
msgInfo.Msg.ContentType = constant.MsgRevokeNotification
msgInfo.Msg.Content = string(content)
}
}
//start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
//n := int32(len(msgs))
//if start >= n {
// return n, []*relation.MsgInfoModel{}, nil
//}
//if start+req.Pagination.ShowNumber < n {
// msgs = msgs[start : start+req.Pagination.ShowNumber]
//} else {
// msgs = msgs[start:]
//}
return count[0], msgs, nil
} }
//func (m *MsgMgo) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*model.MsgInfoModel, error) {
// where := make(bson.A, 0, 6)
// if req.RecvID != "" {
// if req.SessionType == constant.ReadGroupChatType {
// where = append(where, bson.M{
// "$or": bson.A{
// bson.M{"doc_id": "^n_" + req.RecvID + ":"},
// bson.M{"doc_id": "^sg_" + req.RecvID + ":"},
// },
// })
// } else {
// where = append(where, bson.M{"msgs.msg.recv_id": req.RecvID})
// }
// }
// if req.SendID != "" {
// where = append(where, bson.M{"msgs.msg.send_id": req.SendID})
// }
// if req.ContentType != 0 {
// where = append(where, bson.M{"msgs.msg.content_type": req.ContentType})
// }
// if req.SessionType != 0 {
// where = append(where, bson.M{"msgs.msg.session_type": req.SessionType})
// }
// if req.SendTime != "" {
// sendTime, err := time.Parse(time.DateOnly, req.SendTime)
// if err != nil {
// return 0, nil, errs.ErrArgs.WrapMsg("invalid sendTime", "req", req.SendTime, "format", time.DateOnly, "cause", err.Error())
// }
// where = append(where,
// bson.M{
// "msgs.msg.send_time": bson.M{
// "$gte": sendTime.UnixMilli(),
// },
// },
// bson.M{
// "msgs.msg.send_time": bson.M{
// "$lt": sendTime.Add(time.Hour * 24).UnixMilli(),
// },
// },
// )
// }
// opt := options.Find().SetLimit(100)
// res, err := mongoutil.Find[model.MsgDocModel](ctx, m.coll, bson.M{"$and": where}, opt)
// if err != nil {
// return 0, nil, err
// }
// _ = res
// fmt.Println()
//
// return 0, nil, nil
// pipeline := bson.A{
// bson.M{
// "$unwind": "$msgs",
// },
// }
// if len(where) > 0 {
// pipeline = append(pipeline, bson.M{
// "$match": bson.M{"$and": where},
// })
// }
// pipeline = append(pipeline,
// bson.M{
// "$project": bson.M{
// "_id": 0,
// "msg": "$msgs.msg",
// },
// },
// bson.M{
// "$count": "count",
// },
// )
// //count, err := mongoutil.Aggregate[int32](ctx, m.coll, pipeline)
// //if err != nil {
// // return 0, nil, err
// //}
// //if len(count) == 0 || count[0] == 0 {
// // return 0, nil, nil
// //}
// count := []int32{0}
// pipeline = pipeline[:len(pipeline)-1]
// pipeline = append(pipeline,
// bson.M{
// "$skip": (req.Pagination.GetPageNumber() - 1) * req.Pagination.GetShowNumber(),
// },
// bson.M{
// "$limit": req.Pagination.GetShowNumber(),
// },
// )
// msgs, err := mongoutil.Aggregate[*model.MsgInfoModel](ctx, m.coll, pipeline)
// if err != nil {
// return 0, nil, err
// }
// for i := range msgs {
// msgInfo := msgs[i]
// if msgInfo == nil || msgInfo.Msg == nil {
// continue
// }
// if msgInfo.Revoke != nil {
// revokeContent := sdkws.MessageRevokedContent{
// RevokerID: msgInfo.Revoke.UserID,
// RevokerRole: msgInfo.Revoke.Role,
// ClientMsgID: msgInfo.Msg.ClientMsgID,
// RevokerNickname: msgInfo.Revoke.Nickname,
// RevokeTime: msgInfo.Revoke.Time,
// SourceMessageSendTime: msgInfo.Msg.SendTime,
// SourceMessageSendID: msgInfo.Msg.SendID,
// SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
// SessionType: msgInfo.Msg.SessionType,
// Seq: msgInfo.Msg.Seq,
// Ex: msgInfo.Msg.Ex,
// }
// data, err := jsonutil.JsonMarshal(&revokeContent)
// if err != nil {
// return 0, nil, errs.WrapMsg(err, "json.Marshal revokeContent")
// }
// elem := sdkws.NotificationElem{Detail: string(data)}
// content, err := jsonutil.JsonMarshal(&elem)
// if err != nil {
// return 0, nil, errs.WrapMsg(err, "json.Marshal elem")
// }
// msgInfo.Msg.ContentType = constant.MsgRevokeNotification
// msgInfo.Msg.Content = string(content)
// }
// }
// //start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
// //n := int32(len(msgs))
// //if start >= n {
// // return n, []*relation.MsgInfoModel{}, nil
// //}
// //if start+req.Pagination.ShowNumber < n {
// // msgs = msgs[start : start+req.Pagination.ShowNumber]
// //} else {
// // msgs = msgs[start:]
// //}
// return count[0], msgs, nil
//}
func (m *MsgMgo) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*model.UserCount, dateCount map[string]int64, err error) { func (m *MsgMgo) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*model.UserCount, dateCount map[string]int64, err error) {
var sort int var sort int
if ase { if ase {

@ -0,0 +1,75 @@
package mgo
import (
"context"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/db/mongoutil"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"math/rand"
"strconv"
"testing"
"time"
)
func TestName1(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*300)
defer cancel()
cli := Result(mongo.Connect(ctx, options.Client().ApplyURI("mongodb://openIM:openIM123@172.16.8.48:37017/openim_v3?maxPoolSize=100").SetConnectTimeout(5*time.Second)))
v := &MsgMgo{
coll: cli.Database("openim_v3").Collection("msg3"),
}
req := &msg.SearchMessageReq{
//RecvID: "3187706596",
//SendID: "7009965934",
ContentType: 101,
//SendTime: "2024-05-06",
//SessionType: 3,
Pagination: &sdkws.RequestPagination{
PageNumber: 1,
ShowNumber: 10,
},
}
total, res, err := v.SearchMessage(ctx, req)
if err != nil {
panic(err)
}
for i, re := range res {
t.Logf("%d => %d | %+v", i+1, re.Msg.Seq, re.Msg.Content)
}
t.Log(total)
}
func TestName10(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
cli := Result(mongo.Connect(ctx, options.Client().ApplyURI("mongodb://openIM:openIM123@172.16.8.48:37017/openim_v3?maxPoolSize=100").SetConnectTimeout(5*time.Second)))
v := &MsgMgo{
coll: cli.Database("openim_v3").Collection("msg3"),
}
opt := options.Find().SetLimit(1000)
res, err := mongoutil.Find[model.MsgDocModel](ctx, v.coll, bson.M{}, opt)
if err != nil {
panic(err)
}
ctx = context.Background()
for i := 0; i < 100000; i++ {
for j := range res {
res[j].DocID = strconv.FormatUint(rand.Uint64(), 10) + ":0"
}
if err := mongoutil.InsertMany(ctx, v.coll, res); err != nil {
panic(err)
}
t.Log("====>", time.Now(), i)
}
}

@ -26,7 +26,7 @@ func Mongodb() *mongo.Database {
func TestUserSeq(t *testing.T) { func TestUserSeq(t *testing.T) {
uSeq := Result(NewSeqUserMongo(Mongodb())).(*seqUserMongo) uSeq := Result(NewSeqUserMongo(Mongodb())).(*seqUserMongo)
t.Log(uSeq.SetMinSeq(context.Background(), "1000", "2000", 4)) t.Log(uSeq.SetUserMinSeq(context.Background(), "1000", "2000", 4))
} }
func TestConversationSeq(t *testing.T) { func TestConversationSeq(t *testing.T) {
@ -35,3 +35,8 @@ func TestConversationSeq(t *testing.T) {
t.Log(cSeq.Malloc(context.Background(), "2000", 10)) t.Log(cSeq.Malloc(context.Background(), "2000", 10))
t.Log(cSeq.GetMaxSeq(context.Background(), "2000")) t.Log(cSeq.GetMaxSeq(context.Background(), "2000"))
} }
func TestUserGetUserReadSeqs(t *testing.T) {
uSeq := Result(NewSeqUserMongo(Mongodb())).(*seqUserMongo)
t.Log(uSeq.GetUserReadSeqs(context.Background(), "2110910952", []string{"sg_345762580", "2000", "3000"}))
}

@ -68,27 +68,35 @@ func (s *seqUserMongo) getSeq(ctx context.Context, conversationID string, userID
} }
} }
func (s *seqUserMongo) GetMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserMongo) GetUserMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return s.getSeq(ctx, conversationID, userID, "max_seq") return s.getSeq(ctx, conversationID, userID, "max_seq")
} }
func (s *seqUserMongo) SetMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserMongo) SetUserMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
return s.setSeq(ctx, conversationID, userID, seq, "max_seq") return s.setSeq(ctx, conversationID, userID, seq, "max_seq")
} }
func (s *seqUserMongo) GetMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserMongo) GetUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return s.getSeq(ctx, conversationID, userID, "min_seq") return s.getSeq(ctx, conversationID, userID, "min_seq")
} }
func (s *seqUserMongo) SetMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserMongo) SetUserMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
return s.setSeq(ctx, conversationID, userID, seq, "min_seq") return s.setSeq(ctx, conversationID, userID, seq, "min_seq")
} }
func (s *seqUserMongo) GetReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) { func (s *seqUserMongo) GetUserReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
return s.getSeq(ctx, conversationID, userID, "read_seq") return s.getSeq(ctx, conversationID, userID, "read_seq")
} }
func (s *seqUserMongo) GetReadSeqs(ctx context.Context, userID string, conversationID []string) (map[string]int64, error) { func (s *seqUserMongo) notFoundSet0(seq map[string]int64, conversationIDs []string) {
for _, conversationID := range conversationIDs {
if _, ok := seq[conversationID]; !ok {
seq[conversationID] = 0
}
}
}
func (s *seqUserMongo) GetUserReadSeqs(ctx context.Context, userID string, conversationID []string) (map[string]int64, error) {
if len(conversationID) == 0 { if len(conversationID) == 0 {
return map[string]int64{}, nil return map[string]int64{}, nil
} }
@ -102,9 +110,10 @@ func (s *seqUserMongo) GetReadSeqs(ctx context.Context, userID string, conversat
for _, seq := range seqs { for _, seq := range seqs {
res[seq.ConversationID] = seq.ReadSeq res[seq.ConversationID] = seq.ReadSeq
} }
s.notFoundSet0(res, conversationID)
return res, nil return res, nil
} }
func (s *seqUserMongo) SetReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error { func (s *seqUserMongo) SetUserReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error {
return s.setSeq(ctx, conversationID, userID, seq, "read_seq") return s.setSeq(ctx, conversationID, userID, seq, "read_seq")
} }

@ -37,7 +37,7 @@ type Msg interface {
GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*model.MsgDocModel, error) GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*model.MsgDocModel, error)
DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error
MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error
SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*model.MsgInfoModel, error) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int64, []*model.MsgInfoModel, error)
RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*model.UserCount, dateCount map[string]int64, err error) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*model.UserCount, dateCount map[string]int64, err error)
RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*model.GroupCount, dateCount map[string]int64, err error) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*model.GroupCount, dateCount map[string]int64, err error)
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)

@ -3,11 +3,11 @@ package database
import "context" import "context"
type SeqUser interface { type SeqUser interface {
GetMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserMaxSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserMaxSeq(ctx context.Context, conversationID string, userID string, seq int64) error
GetMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserMinSeq(ctx context.Context, conversationID string, userID string, seq int64) error
GetReadSeq(ctx context.Context, conversationID string, userID string) (int64, error) GetUserReadSeq(ctx context.Context, conversationID string, userID string) (int64, error)
SetReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error SetUserReadSeq(ctx context.Context, conversationID string, userID string, seq int64) error
GetReadSeqs(ctx context.Context, userID string, conversationID []string) (map[string]int64, error) GetUserReadSeqs(ctx context.Context, userID string, conversationID []string) (map[string]int64, error)
} }

@ -324,6 +324,10 @@ func (s *NotificationSender) send(ctx context.Context, sendID, recvID string, co
options := config.GetOptionsByNotification(optionsConfig) options := config.GetOptionsByNotification(optionsConfig)
s.SetOptionsByContentType(ctx, options, contentType) s.SetOptionsByContentType(ctx, options, contentType)
msg.Options = options msg.Options = options
// fill Notification OfflinePush by config
offlineInfo.Title = optionsConfig.OfflinePush.Title
offlineInfo.Desc = optionsConfig.OfflinePush.Desc
offlineInfo.Ex = optionsConfig.OfflinePush.Ext
msg.OfflinePushInfo = &offlineInfo msg.OfflinePushInfo = &offlineInfo
req.MsgData = &msg req.MsgData = &msg
_, err = s.sendMsg(ctx, &req) _, err = s.sendMsg(ctx, &req)

@ -130,14 +130,14 @@ func Main(conf string, del time.Duration) error {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return uSeq.GetReadSeq(ctx, conversationID, userID) return uSeq.GetUserReadSeq(ctx, conversationID, userID)
}, },
SetSeq: func(ctx context.Context, id string, seq int64) error { SetSeq: func(ctx context.Context, id string, seq int64) error {
conversationID, userID, err := uSpitHasReadSeq(id) conversationID, userID, err := uSpitHasReadSeq(id)
if err != nil { if err != nil {
return err return err
} }
return uSeq.SetReadSeq(ctx, conversationID, userID, seq) return uSeq.SetUserReadSeq(ctx, conversationID, userID, seq)
}, },
}, },
{ {
@ -147,14 +147,14 @@ func Main(conf string, del time.Duration) error {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return uSeq.GetMinSeq(ctx, conversationID, userID) return uSeq.GetUserMinSeq(ctx, conversationID, userID)
}, },
SetSeq: func(ctx context.Context, id string, seq int64) error { SetSeq: func(ctx context.Context, id string, seq int64) error {
conversationID, userID, err := uSpitConversationUserMinSeq(id) conversationID, userID, err := uSpitConversationUserMinSeq(id)
if err != nil { if err != nil {
return err return err
} }
return uSeq.SetMinSeq(ctx, conversationID, userID, seq) return uSeq.SetUserMinSeq(ctx, conversationID, userID, seq)
}, },
}, },
} }

Loading…
Cancel
Save