Merge remote-tracking branch 'origin/3.6.1-code-conventions' into 3.6.1-code-conventions

This commit is contained in:
Gordon 2024-04-15 15:02:53 +08:00
commit a850733920
63 changed files with 777 additions and 1044 deletions

View File

@ -372,7 +372,7 @@ linters-settings:
# Default: [] # Default: []
ignored-functions: ignored-functions:
- '^math\.' - '^math\.'
- '^http\.StatusText$' - '^webhook\.StatusText$'
gomoddirectives: gomoddirectives:
# Allow local `replace` directives. Default is false. # Allow local `replace` directives. Default is false.
replace-local: true replace-local: true

View File

@ -1,243 +1,67 @@
# OpenIM Configuration Guide 配置文件说明
<!-- vscode-markdown-toc --> 每个组件单独有一个配置文件,主要是地址及账号密码信息。
* 1. [Directory Structure and File Descriptions](#DirectoryStructureandFileDescriptions)
* 1.1. [Directory Structure](#DirectoryStructure)
* 1.2. [Directory Structure Explanation](#DirectoryStructureExplanation)
* 2. [File Descriptions](#FileDescriptions)
* 2.1. [Files in the Root Directory](#FilesintheRootDirectory)
* 2.2. [Files in the `templates/` Directory](#FilesinthetemplatesDirectory)
* 3. [Configuration File Generation](#ConfigurationFileGeneration)
* 3.1. [How to Use `init-config.sh` Script](#HowtoUseinit-config.shScript)
* 3.2. [Examples of Operations](#ExamplesofOperations)
* 3.3. [Points to Note](#PointstoNote)
* 4. [Example Directory](#ExampleDirectory)
* 4.1. [Overview](#Overview)
* 4.2. [Structure](#Structure)
* 4.3. [How to Use These Examples](#HowtoUseTheseExamples)
* 4.4. [Tips for Using Example Files:](#TipsforUsingExampleFiles:)
* 5. [Configuration Item Descriptions](#ConfigurationItemDescriptions)
* 6. [Version Management and Upgrading](#VersionManagementandUpgrading)
* 6.1. [Pulling the Latest Code](#PullingtheLatestCode)
* 6.2. [Generating the Latest Example Configuration Files](#GeneratingtheLatestExampleConfigurationFiles)
* 6.3. [Comparing Configuration File Differences](#ComparingConfigurationFileDifferences)
* 6.4. [Updating Configuration Files](#UpdatingConfigurationFiles)
* 6.5. [Updating Binary Files and Restarting Services](#UpdatingBinaryFilesandRestartingServices)
* 6.6. [Best Practices for Version Management](#BestPracticesforVersionManagement)
* 7. [How to Contribute](#HowtoContribute)
* 7.1. [OpenIM Configuration Item Descriptions](#OpenIMConfigurationItemDescriptions)
* 7.2. [Modifying Template Files](#ModifyingTemplateFiles)
* 7.3. [Updating Configuration Center Scripts](#UpdatingConfigurationCenterScripts)
* 7.4. [Configuration File Generation Process](#ConfigurationFileGenerationProcess)
* 7.5. [Contribution Guidelines](#ContributionGuidelines)
* 7.6. [Submission and Review](#SubmissionandReview)
<!-- vscode-markdown-toc-config
numbering=true
autoSave=true
/vscode-markdown-toc-config -->
<!-- /vscode-markdown-toc -->
## 1. <a name='DirectoryStructureandFileDescriptions'></a>Directory Structure and File Descriptions
This document details the structure of the `config` directory, aiding users in understanding and managing configuration files.
### 1.1. <a name='DirectoryStructure'></a>Directory Structure
```bash callback
$ tree config
├── alertmanager.yml
├── config.yaml
├── email.tmpl
├── instance-down-rules.yml
├── notification.yaml rpc共同配置项说明
├── prometheus.yml
├── Readme.md ```
└── templates rpc:
├── alertmanager.yml.template #api或其他rpc可通过这个ip访问到此rpc如果为空则获取内网ip默认为空即可
├── config.yaml.template registerIP: ''
├── email.tmpl.template #监听ip如果为0.0.0.0则内外网ip都监听为空则自动获取内网ip监听
├── env.template listenIP: 0.0.0.0
├── instance-down-rules.yml.template #监听端口如果配置多个则会启动多个实例但需和prometheus.ports保持一致
├── notification.yaml.template ports: [ 10120 ]
├── open-im-ng-example.conf
├── prometheus-dashboard.yaml prometheus:
└── prometheus.yml.template enable: true
#prometheus
ports: [ 20104 ]
``` ```
### 1.2. <a name='DirectoryStructureExplanation'></a>Directory Structure Explanation
- **Root Directory (`config/`)**: Contains actual configuration files and the `templates` subdirectory.
- **`templates/` Subdirectory**: Stores configuration templates for generating or updating configuration files in the root directory.
## 2. <a name='FileDescriptions'></a>File Descriptions api配置项说明
### 2.1. <a name='FilesintheRootDirectory'></a>Files in the Root Directory ```
api:
#0.0.0.0表示内外网ip都监听不应该修改
listenIP: 0.0.0.0
#监听端口,如果配置多个则会启动多个实例,和prometheus.ports保持一致
ports: [ 10002 ]
- **`alertmanager.yml`**: Configuration file for AlertManager, managing and setting up the alert system. prometheus:
- **`config.yaml`**: The main application configuration file, covering service settings. enable: true
- **`email.tmpl`**: Template file for email notifications, defining email format and content. ports: [ 20113 ]
- **`instance-down-rules.yml`**: Instance downtime rules configuration file for the monitoring system. #怎么描述 grafanaURL地址 外网地址,通过浏览器能访问到
- **`notification.yaml`**: Configuration file for notification settings, defining different types of notifications. grafanaURL: http://127.0.0.1:13000/
- **`prometheus.yml`**: Configuration file for the Prometheus monitoring system, setting monitoring metrics and rules.
### 2.2. <a name='FilesinthetemplatesDirectory'></a>Files in the `templates/` Directory
- **`alertmanager.yml.template`**: Template for AlertManager configuration.
- **`config.yaml.template`**: Main configuration template for the application.
- **`email.tmpl.template`**: Template for email notifications.
- **`env.template`**: Template for environmental variable configurations, setting environment-related configurations.
- **`instance-down-rules.yml.template`**: Template for instance downtime rules.
- **`notification.yaml.template`**: Template for notification settings.
- **`open-im-ng-example.conf`**: Example configuration file for the application.
- **`prometheus-dashboard.yaml`**: Prometheus dashboard configuration file, specific to the OpenIM application.
- **`prometheus.yml.template`**: Template for Prometheus configuration.
## 3. <a name='ConfigurationFileGeneration'></a>Configuration File Generation
Configuration files can be automatically generated using the `make init` command or the `./scripts/init-config.sh` script. These scripts conveniently extract templates from the `templates` directory and generate or update actual configuration files in the root directory.
### 3.1. <a name='HowtoUseinit-config.shScript'></a>How to Use `init-config.sh` Script
```bash
$ ./scripts/init-config.sh --help
Usage: init-config.sh [options]
Options:
-h, --help Show this help message
--force Overwrite existing files without prompt
--skip Skip generation if file exists
--examples Generate example files
--clean-config Clean all configuration files
--clean-examples Clean all example files
``` ```
### 3.2. <a name='ExamplesofOperations'></a>Examples of Operations
- Generate all template configuration files:
```bash log配置项说明
$ ./scripts/init-config.sh --examples
```
- Force overwrite existing configuration files: ```
#log存放路径,如果要修改则改为全路径
```bash storageLocation: ../../../../logs/
$ ./scripts/init-config.sh --force rotationTime: 24
``` remainRotationCount: 2
#3: 生产环境; 6日志较多调试环境
### 3.3. <a name='PointstoNote'></a>Points to Note remainLogLevel: 6
isStdout: false
- **Template files should not be directly modified**: Files in the `template` directory are templates included in source code management. Direct modification may lead to version conflicts or management issues. isJson: false
- **Operations for Windows Users**: Windows users can use the `cp` command to copy files from the `template` directory to the `config/` directory and then modify the configuration items as needed. withStack: false
## 4. <a name='ExampleDirectory'></a>Example Directory
Welcome to our project's `examples` directory! This directory contains a range of example files, showcasing various configurations and settings of our software. These examples are intended to provide you with templates that can serve as a starting point for your own configurations.
### 4.1. <a name='Overview'></a>Overview
In this directory, you'll find examples suitable for a variety of use cases. Each file is a template with default values and configurations, demonstrating best practices and typical scenarios. Whether you're just getting started or looking to implement complex settings, these examples should help you get on the right track.
### 4.2. <a name='Structure'></a>Structure
Here's a quick overview of the contents in this directory:
- `env-example.yaml`: Demonstrates how to set up environmental variables.
- `openim-example.yaml`: Example configuration file for the OpenIM application.
- `prometheus-example.yml`: Example configuration for monitoring with Prometheus.
- `alertmanager-example.yml`: Template for setting up Alertmanager configuration.
### 4.3. <a name='HowtoUseTheseExamples'></a>How to Use These Examples
To use these examples, simply copy the relevant files to your working directory and rename them as needed (for example, removing the `-example` suffix). Then, modify the files according to your needs.
### 4.4. <a name='TipsforUsingExampleFiles:'></a>Tips for Using Example Files:
1. **Read Comments**: Each file contains comments explaining the various sections and settings. Make sure to read these comments for a better understanding of how to customize the file.
2. **Check Required Changes**: Some examples might require mandatory changes before they can be used effectively (such as setting specific environmental variables).
3. **Version Compatibility**: Ensure that the example files are compatible with the version of the software you are using.
## 5. <a name='ConfigurationItemDescriptions'></a>Configuration Item Descriptions
## 6. <a name='VersionManagementandUpgrading'></a>Version Management and Upgrading
When managing and upgrading the `config` directory's versions, it is crucial to ensure that the configuration files in both the local `config/` and `config/templates/` directories are kept in sync. This process can ensure that your configuration files are consistent with the latest standard templates, while also maintaining custom settings.
### 6.1. <a name='PullingtheLatestCode'></a>Pulling the Latest Code
First, ensure that your local repository is in sync with the remote repository. This can be achieved by pulling the latest code:
```bash
$ git pull
``` ```
### 6.2. <a name='GeneratingtheLatestExampleConfigurationFiles'></a>Generating the Latest Example Configuration Files
Next, generate the latest example configuration files. This can be done by running the `init-config.sh` script, using the `--examples` option to generate example files, and the `--skip` option to avoid overwriting existing configuration files:
```bash
$ ./scripts/init-config.sh --examples --skip
```
### 6.3. <a name='ComparingConfigurationFileDifferences'></a>Comparing Configuration File Differences
Once the latest example configuration files are generated, you need to compare the configuration files in the `config/` and `config/templates/` directories to find any potential differences. This step ensures that you can identify and integrate any important updates or changes. Tools like `diff` can be helpful in completing this step:
```bash
$ diff -ur config/ config/templates/
```
### 6.4. <a name='UpdatingConfigurationFiles'></a>Updating Configuration Files
Based on the comparison results, manually update the configuration files in the `config/` directory to reflect the latest configurations in `config/templates/`. During this process, ensure to retain any custom configuration settings.
### 6.5. <a name='UpdatingBinaryFilesandRestartingServices'></a>Updating Binary Files and Restarting Services
After updating the configuration files, the next step is to update any related binary files. This typically involves downloading and installing the latest version of the application or service. Depending on the specific application or service, this might involve running specific update scripts or directly downloading the latest version from official sources.
Once the binary files are updated, the services need to be restarted to apply the new configurations. Make sure to conduct necessary checks before restarting to ensure the correctness of the configurations.
### 6.6. <a name='BestPracticesforVersionManagement'></a>Best Practices for Version Management
- **Record Changes**: When committing changes to a version control system, ensure to log detailed change logs.
- **Stay Synced**: Regularly sync with the remote repository to ensure that your local configurations are in line with the latest developments.
- **Backup**: Backup your current configurations before making any significant changes, so that you can revert to a previous state if necessary.
By following these steps and best practices, you can ensure effective management and smooth upgrading of your `config` directory.
## 7. <a name='HowtoContribute'></a>How to Contribute
If you have an understanding of the logic behind OpenIM's configuration generation, then you will clearly know where to make modifications to contribute code.
### 7.1. <a name='OpenIMConfigurationItemDescriptions'></a>OpenIM Configuration Item Descriptions
First, it is recommended to read the [OpenIM Configuration Items Document](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md). This will help you understand the roles of various configuration items and how they affect the operation of OpenIM.
### 7.2. <a name='ModifyingTemplateFiles'></a>Modifying Template Files
To contribute to OpenIM, focus on the `./deployments/templates` directory. This contains various configuration template files, which are the basis for generating the final configuration files.
When making modifications, ensure that your changes align with OpenIM's configuration requirements and logic. This may involve adding new template files or modifying existing files to reflect new configuration options or structural changes.
### 7.3. <a name='UpdatingConfigurationCenterScripts'></a>Updating Configuration Center Scripts
In addition to modifying template files, pay attention to the `./scripts/install/environment.sh` script. In this script, you may need to add or modify environment variables.
This script is responsible for defining environment variables that influence configuration generation. Therefore, any new configuration items or modifications to existing items need to be reflected here.
### 7.4. <a name='ConfigurationFileGenerationProcess'></a>Configuration File Generation Process
The essence of the `make init` command is to use the environment variables defined in `/scripts/install/environment.sh` to render the template files in the `./deployments/templates` directory, thereby generating the final configuration files.
When contributing code, ensure that your changes work smoothly in this process and do not cause errors during configuration file generation.
### 7.5. <a name='ContributionGuidelines'></a>Contribution Guidelines
- **Code Review**: Ensure your changes have passed code review. This typically means that the code should be clear, easy to understand, and adhere to the project's coding style and best practices.
- **Testing**: Before submitting changes, conduct thorough tests to ensure new or modified configurations work as expected and do not negatively impact existing functionalities.
- **Documentation**: If you have added a new configuration option or made significant changes to an existing one, update the relevant documentation to assist other users and developers in understanding and utilizing these changes.
### 7.6. <a name='SubmissionandReview'></a>Submission and Review
After completing your changes, submit your code to the OpenIM repository in the form of a Pull Request (PR). The PR will be reviewed by the project maintainers and you may be asked to make further modifications or provide additional information.

View File

@ -90,9 +90,9 @@ groupApplicationRejected:
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: false enable: false
title: " title" title: "groupApplicationRejected title"
desc: " desc" desc: "groupApplicationRejected desc"
ext: " ext" ext: "groupApplicationRejected ext"
groupOwnerTransferred: groupOwnerTransferred:

View File

@ -5,4 +5,4 @@ api:
prometheus: prometheus:
enable: true enable: true
ports: [ 20113 ] ports: [ 20113 ]
grafanaURL: http://127.0.0.1:13000/ grafanaURL: webhook://127.0.0.1:13000/

View File

@ -8,5 +8,6 @@ prometheus:
ports: [ 20106 ] ports: [ 20106 ]
tokenPolicy: tokenPolicy:
#token有效期单位
expire: 90 expire: 90

View File

@ -7,7 +7,10 @@ prometheus:
enable: true enable: true
ports: [ 20102 ] ports: [ 20102 ]
#发消息是否需要好友验证
friendVerify: false friendVerify: false
#
groupMessageHasReadReceiptEnable: true groupMessageHasReadReceiptEnable: true
singleMessageHasReadReceiptEnable: true singleMessageHasReadReceiptEnable: true

View File

@ -24,9 +24,9 @@ object:
sessionToken: '' sessionToken: ''
publicRead: false publicRead: false
kodo: kodo:
endpoint: "http://s3.cn-east-1.qiniucs.com" endpoint: "webhook://s3.cn-east-1.qiniucs.com"
bucket: "demo-9999999" bucket: "demo-9999999"
bucketURL: "http://your.domain.com" bucketURL: "webhook://your.domain.com"
accessKeyID: '' accessKeyID: ''
accessKeySecret: '' accessKeySecret: ''
sessionToken: '' sessionToken: ''

View File

@ -1213,7 +1213,7 @@
"editorMode": "code", "editorMode": "code",
"expr": "sum(rate(app_requests_total{job=~\"^($job)$\"}[$interval])) by (job)", "expr": "sum(rate(app_requests_total{job=~\"^($job)$\"}[$interval])) by (job)",
"instant": false, "instant": false,
"legendFormat": "{{job}}-http", "legendFormat": "{{job}}-webhook",
"range": true, "range": true,
"refId": "A" "refId": "A"
}, },

View File

@ -1,4 +1,4 @@
url: "http://127.0.0.1:10008/callbackExample" url: "webhook://127.0.0.1:10008/callbackExample"
beforeSendSingleMsg: beforeSendSingleMsg:
enable: false enable: false
timeout: 5 timeout: 5
@ -10,11 +10,9 @@ beforeUpdateUserInfoEx:
afterUpdateUserInfoEx: afterUpdateUserInfoEx:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterSendSingleMsg: afterSendSingleMsg:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeSendGroupMsg: beforeSendGroupMsg:
enable: false enable: false
timeout: 5 timeout: 5
@ -26,19 +24,15 @@ beforeMsgModify:
afterSendGroupMsg: afterSendGroupMsg:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterUserOnline: afterUserOnline:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterUserOffline: afterUserOffline:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterUserKickOff: afterUserKickOff:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeOfflinePush: beforeOfflinePush:
enable: false enable: false
timeout: 5 timeout: 5
@ -62,7 +56,6 @@ beforeUpdateUserInfo:
afterUpdateUserInfo: afterUpdateUserInfo:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeCreateGroup: beforeCreateGroup:
enable: false enable: false
timeout: 5 timeout: 5
@ -70,7 +63,6 @@ beforeCreateGroup:
afterCreateGroup: afterCreateGroup:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeMemberJoinGroup: beforeMemberJoinGroup:
enable: false enable: false
timeout: 5 timeout: 5
@ -82,19 +74,15 @@ beforeSetGroupMemberInfo:
afterSetGroupMemberInfo: afterSetGroupMemberInfo:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterQuitGroup: afterQuitGroup:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterKickGroupMember: afterKickGroupMember:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterDismissGroup: afterDismissGroup:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeApplyJoinGroup: beforeApplyJoinGroup:
enable: false enable: false
timeout: 5 timeout: 5
@ -102,11 +90,9 @@ beforeApplyJoinGroup:
afterGroupMsgRead: afterGroupMsgRead:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterSingleMsgRead: afterSingleMsgRead:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeUserRegister: beforeUserRegister:
enable: false enable: false
timeout: 5 timeout: 5
@ -114,11 +100,9 @@ beforeUserRegister:
afterUserRegister: afterUserRegister:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterTransferGroupOwner: afterTransferGroupOwner:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeSetFriendRemark: beforeSetFriendRemark:
enable: false enable: false
timeout: 5 timeout: 5
@ -126,15 +110,12 @@ beforeSetFriendRemark:
afterSetFriendRemark: afterSetFriendRemark:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterGroupMsgRevoke: afterGroupMsgRevoke:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterJoinGroup: afterJoinGroup:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeInviteUserToGroup: beforeInviteUserToGroup:
enable: false enable: false
timeout: 5 timeout: 5
@ -142,7 +123,6 @@ beforeInviteUserToGroup:
afterSetGroupInfo: afterSetGroupInfo:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeSetGroupInfo: beforeSetGroupInfo:
enable: false enable: false
timeout: 5 timeout: 5
@ -150,15 +130,13 @@ beforeSetGroupInfo:
afterRevokeMsg: afterRevokeMsg:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeAddBlack: beforeAddBlack:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true failedContinue:
afterAddFriend: afterAddFriend:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeAddFriendAgree: beforeAddFriendAgree:
enable: false enable: false
timeout: 5 timeout: 5
@ -166,7 +144,6 @@ beforeAddFriendAgree:
afterDeleteFriend: afterDeleteFriend:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
beforeImportFriends: beforeImportFriends:
enable: false enable: false
timeout: 5 timeout: 5
@ -174,8 +151,6 @@ beforeImportFriends:
afterImportFriends: afterImportFriends:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true
afterRemoveBlack: afterRemoveBlack:
enable: false enable: false
timeout: 5 timeout: 5
failedContinue: true

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-api.selectorLabels" . | nindent 4 }} {{- include "openim-api.selectorLabels" . | nindent 4 }}

View File

@ -48,7 +48,7 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
- name: rpc - name: rpc
@ -57,11 +57,11 @@ spec:
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,9 +22,9 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
- port: 88 - port: 88
targetPort: rpc targetPort: rpc
protocol: TCP protocol: TCP

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-msgtransfer.selectorLabels" . | nindent 4 }} {{- include "openim-msgtransfer.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-push.selectorLabels" . | nindent 4 }} {{- include "openim-push.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-auth.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-auth.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-conversation.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-conversation.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-friend.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-friend.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-group.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-group.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-msg.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-msg.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-third.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-third.selectorLabels" . | nindent 4 }}

View File

@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: webhook
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
#livenessProbe: #livenessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
#readinessProbe: #readinessProbe:
# httpGet: # httpGet:
# path: / # path: /
# port: http # port: webhook
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
volumeMounts: volumeMounts:

View File

@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
- port: {{ .Values.service.port }} - port: {{ .Values.service.port }}
targetPort: http targetPort: webhook
protocol: TCP protocol: TCP
name: http name: webhook
selector: selector:
{{- include "openim-rpc-user.selectorLabels" . | nindent 4 }} {{- include "openim-rpc-user.selectorLabels" . | nindent 4 }}

View File

@ -323,7 +323,7 @@ iosPush:
# Timeout in seconds # Timeout in seconds
# Whether to continue execution if callback fails # Whether to continue execution if callback fails
callback: callback:
url: "http://127.0.0.1:10008/callbackExample" url: "webhook://127.0.0.1:10008/callbackExample"
beforeSendSingleMsg: beforeSendSingleMsg:
enable: ${CALLBACK_ENABLE} enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT} timeout: ${CALLBACK_TIMEOUT}

View File

@ -362,7 +362,7 @@ func SIGTERMExit() {
```go ```go
import ( import (
_ "net/http/pprof" _ "net/webhook/pprof"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"

View File

@ -581,7 +581,7 @@ func SIGTERMExit() {
```go ```go
import ( import (
_ "net/http/pprof" _ "net/webhook/pprof"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"

View File

@ -470,7 +470,7 @@ wrappedErr := errs.WrapMsg(err, "additional error information")
```go ```go
// "github.com/openimsdk/tools/errs" // "github.com/openimsdk/tools/errs"
err := errors.New("original error") err := errors.New("original error")
wrappedErr := errs.WrapMsg(err, "problem occurred", "code", 404, "url", "http://example.com") wrappedErr := errs.WrapMsg(err, "problem occurred", "code", 404, "url", "webhook://example.com")
// wrappedErr will contain the original error, call stack, and "problem occurred code=404, url=http://example.com" // wrappedErr will contain the original error, call stack, and "problem occurred code=404, url=http://example.com"
``` ```
@ -494,7 +494,7 @@ Suppose we have some runtime context variables, such as a user ID and the type o
userID := "user123" userID := "user123"
operation := "update profile" operation := "update profile"
errorCode := 500 errorCode := 500
requestURL := "http://example.com/updateProfile" requestURL := "webhook://example.com/updateProfile"
// Create a new error // Create a new error
err := errors.New("original error") err := errors.New("original error")

View File

@ -65,7 +65,7 @@ func setURLPrefix(c *gin.Context, urlPrefix *string) error {
} }
} }
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "webhook",
Host: c.Request.Host, Host: c.Request.Host,
Path: "/object/", Path: "/object/",
} }

View File

@ -20,19 +20,15 @@ import (
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/http"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
) )
func CallbackUserOnline(ctx context.Context, callback *config.Webhooks, userID string, platformID int, isAppBackground bool, connID string) error { func (ws *WsServer) webhookAfterUserOnline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, isAppBackground bool, connID string) {
if !callback.AfterUserOnline.Enable {
return nil
}
req := cbapi.CallbackUserOnlineReq{ req := cbapi.CallbackUserOnlineReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{ UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{ UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
CallbackCommand: cbapi.CallbackUserOnlineCommand, CallbackCommand: cbapi.CallbackAfterUserOnlineCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID, PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID), Platform: constant.PlatformIDToName(platformID),
@ -43,21 +39,14 @@ func CallbackUserOnline(ctx context.Context, callback *config.Webhooks, userID s
IsAppBackground: isAppBackground, IsAppBackground: isAppBackground,
ConnID: connID, ConnID: connID,
} }
resp := cbapi.CommonCallbackResp{} ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, &req, &resp, callback.AfterUserOnline); err != nil {
return err
}
return nil
} }
func CallbackUserOffline(ctx context.Context, callback *config.Webhooks, userID string, platformID int, connID string) error { func (ws *WsServer) webhookAfterUserOffline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, connID string) {
if !callback.AfterUserOffline.Enable {
return nil
}
req := &cbapi.CallbackUserOfflineReq{ req := &cbapi.CallbackUserOfflineReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{ UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{ UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
CallbackCommand: cbapi.CallbackUserOfflineCommand, CallbackCommand: cbapi.CallbackAfterUserOfflineCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID, PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID), Platform: constant.PlatformIDToName(platformID),
@ -67,21 +56,14 @@ func CallbackUserOffline(ctx context.Context, callback *config.Webhooks, userID
Seq: time.Now().UnixMilli(), Seq: time.Now().UnixMilli(),
ConnID: connID, ConnID: connID,
} }
resp := &cbapi.CallbackUserOfflineResp{} ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackUserOfflineResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterUserOffline); err != nil {
return err
}
return nil
} }
func CallbackUserKickOff(ctx context.Context, callback *config.Webhooks, userID string, platformID int) error { func (ws *WsServer) webhookAfterUserKickOff(ctx context.Context, after *config.AfterConfig, userID string, platformID int) {
if !callback.AfterUserKickOff.Enable {
return nil
}
req := &cbapi.CallbackUserKickOffReq{ req := &cbapi.CallbackUserKickOffReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{ UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{ UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
CallbackCommand: cbapi.CallbackUserKickOffCommand, CallbackCommand: cbapi.CallbackAfterUserKickOffCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID, PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID), Platform: constant.PlatformIDToName(platformID),
@ -90,9 +72,5 @@ func CallbackUserKickOff(ctx context.Context, callback *config.Webhooks, userID
}, },
Seq: time.Now().UnixMilli(), Seq: time.Now().UnixMilli(),
} }
resp := &cbapi.CommonCallbackResp{} ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterUserOffline); err != nil {
return err
}
return nil
} }

View File

@ -17,6 +17,8 @@ package msggateway
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
pbAuth "github.com/openimsdk/protocol/auth" pbAuth "github.com/openimsdk/protocol/auth"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"net/http" "net/http"
@ -52,6 +54,11 @@ type LongConnServer interface {
MessageHandler MessageHandler
} }
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type WsServer struct { type WsServer struct {
msgGatewayConfig *Config msgGatewayConfig *Config
port int port int
@ -72,6 +79,7 @@ type WsServer struct {
Compressor Compressor
Encoder Encoder
MessageHandler MessageHandler
webhookClient *webhook.Client
} }
type kickHandler struct { type kickHandler struct {
@ -95,15 +103,9 @@ func (ws *WsServer) SetUserOnlineStatus(ctx context.Context, client *Client, sta
} }
switch status { switch status {
case constant.Online: case constant.Online:
err := CallbackUserOnline(ctx, &ws.msgGatewayConfig.WebhooksConfig, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID()) ws.webhookAfterUserOnline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOnline, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID())
if err != nil {
log.ZWarn(ctx, "CallbackUserOnline err", err)
}
case constant.Offline: case constant.Offline:
err := CallbackUserOffline(ctx, &ws.msgGatewayConfig.WebhooksConfig, client.UserID, client.PlatformID, client.ctx.GetConnID()) ws.webhookAfterUserOffline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOffline, client.UserID, client.PlatformID, client.ctx.GetConnID())
if err != nil {
log.ZWarn(ctx, "CallbackUserOffline err", err)
}
} }
} }
@ -147,6 +149,7 @@ func NewWsServer(msgGatewayConfig *Config, opts ...Option) (*WsServer, error) {
clients: newUserMap(), clients: newUserMap(),
Compressor: NewGzipCompressor(), Compressor: NewGzipCompressor(),
Encoder: NewGobEncoder(), Encoder: NewGobEncoder(),
webhookClient: webhook.NewWebhookClient(msgGatewayConfig.WebhooksConfig.URL, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)),
}, nil }, nil
} }

View File

@ -19,21 +19,20 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/http"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
) )
func callbackOfflinePush(ctx context.Context, callback *config.Webhooks, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error { func (p *Pusher) webhookBeforeOfflinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error {
if !callback.BeforeOfflinePush.Enable || msg.ContentType == constant.Typing { if msg.ContentType == constant.Typing {
return nil return nil
} }
req := &callbackstruct.CallbackBeforePushReq{ req := &callbackstruct.CallbackBeforePushReq{
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{ UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
CallbackCommand: callbackstruct.CallbackOfflinePushCommand, CallbackCommand: callbackstruct.CallbackBeforeOfflinePushCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: int(msg.SenderPlatformID), PlatformID: int(msg.SenderPlatformID),
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)), Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
@ -51,7 +50,8 @@ func callbackOfflinePush(ctx context.Context, callback *config.Webhooks, userIDs
} }
resp := &callbackstruct.CallbackBeforePushResp{} resp := &callbackstruct.CallbackBeforePushResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeOfflinePush); err != nil {
if err := p.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
return err return err
} }
@ -64,14 +64,14 @@ func callbackOfflinePush(ctx context.Context, callback *config.Webhooks, userIDs
return nil return nil
} }
func callbackOnlinePush(ctx context.Context, callback *config.Webhooks, userIDs []string, msg *sdkws.MsgData) error { func (p *Pusher) webhookBeforeOnlinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData) error {
if !callback.BeforeOnlinePush.Enable || datautil.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing { if datautil.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing {
return nil return nil
} }
req := callbackstruct.CallbackBeforePushReq{ req := callbackstruct.CallbackBeforePushReq{
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{ UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
CallbackCommand: callbackstruct.CallbackOnlinePushCommand, CallbackCommand: callbackstruct.CallbackBeforeOnlinePushCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: int(msg.SenderPlatformID), PlatformID: int(msg.SenderPlatformID),
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)), Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
@ -87,25 +87,25 @@ func callbackOnlinePush(ctx context.Context, callback *config.Webhooks, userIDs
Content: GetContent(msg), Content: GetContent(msg),
} }
resp := &callbackstruct.CallbackBeforePushResp{} resp := &callbackstruct.CallbackBeforePushResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeOnlinePush); err != nil { if err := p.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
return err return err
} }
return nil return nil
} }
func callbackBeforeSuperGroupOnlinePush( func (p *Pusher) webhookBeforeGroupOnlinePush(
ctx context.Context, ctx context.Context,
callback *config.Webhooks, before *config.BeforeConfig,
groupID string, groupID string,
msg *sdkws.MsgData, msg *sdkws.MsgData,
pushToUserIDs *[]string, pushToUserIDs *[]string,
) error { ) error {
if !callback.BeforeGroupOnlinePush.Enable || msg.ContentType == constant.Typing { if msg.ContentType == constant.Typing {
return nil return nil
} }
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{ req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
CallbackCommand: callbackstruct.CallbackSuperGroupOnlinePushCommand, CallbackCommand: callbackstruct.CallbackBeforeGroupOnlinePushCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
PlatformID: int(msg.SenderPlatformID), PlatformID: int(msg.SenderPlatformID),
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)), Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
@ -120,10 +120,9 @@ func callbackBeforeSuperGroupOnlinePush(
Seq: msg.Seq, Seq: msg.Seq,
} }
resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{} resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeGroupOnlinePush); err != nil { if err := p.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
return err return err
} }
if len(resp.UserIDs) != 0 { if len(resp.UserIDs) != 0 {
*pushToUserIDs = resp.UserIDs *pushToUserIDs = resp.UserIDs
} }

View File

@ -17,6 +17,8 @@ package push
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"sync" "sync"
@ -47,6 +49,11 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type Pusher struct { type Pusher struct {
config *Config config *Config
database controller.PushDatabase database controller.PushDatabase
@ -57,6 +64,7 @@ type Pusher struct {
msgRpcClient *rpcclient.MessageRpcClient msgRpcClient *rpcclient.MessageRpcClient
conversationRpcClient *rpcclient.ConversationRpcClient conversationRpcClient *rpcclient.ConversationRpcClient
groupRpcClient *rpcclient.GroupRpcClient groupRpcClient *rpcclient.GroupRpcClient
webhookClient *webhook.Client
} }
var errNoOfflinePusher = errs.New("no offlinePusher is configured") var errNoOfflinePusher = errs.New("no offlinePusher is configured")
@ -75,6 +83,7 @@ func NewPusher(config *Config, discov discovery.SvcDiscoveryRegistry, offlinePus
msgRpcClient: msgRpcClient, msgRpcClient: msgRpcClient,
conversationRpcClient: conversationRpcClient, conversationRpcClient: conversationRpcClient,
groupRpcClient: groupRpcClient, groupRpcClient: groupRpcClient,
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)),
} }
} }
@ -104,9 +113,11 @@ func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID
func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
if err := callbackOnlinePush(ctx, &p.config.WebhooksConfig, userIDs, msg); err != nil {
if err := p.webhookBeforeOnlinePush(ctx, &p.config.WebhooksConfig.BeforeOnlinePush, userIDs, msg); err != nil {
return err return err
} }
// push // push
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs) wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs)
if err != nil { if err != nil {
@ -132,7 +143,7 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg
}) })
if len(offlinePushUserIDList) > 0 { if len(offlinePushUserIDList) > 0 {
if err = callbackOfflinePush(ctx, &p.config.WebhooksConfig, offlinePushUserIDList, msg, &[]string{}); err != nil { if err = p.webhookBeforeOfflinePush(ctx, &p.config.WebhooksConfig.BeforeOfflinePush, offlinePushUserIDList, msg, &[]string{}); err != nil {
return err return err
} }
err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList) err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList)
@ -165,7 +176,7 @@ func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string,
} }
if len(needOfflinePushUserIDs) > 0 { if len(needOfflinePushUserIDs) > 0 {
var offlinePushUserIDs []string var offlinePushUserIDs []string
err := callbackOfflinePush(ctx, &p.config.WebhooksConfig, needOfflinePushUserIDs, msg, &offlinePushUserIDs) err := p.webhookBeforeOfflinePush(ctx, &p.config.WebhooksConfig.BeforeOfflinePush, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
if err != nil { if err != nil {
return err return err
} }
@ -196,7 +207,8 @@ func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string,
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
var pushToUserIDs []string var pushToUserIDs []string
if err = callbackBeforeSuperGroupOnlinePush(ctx, &p.config.WebhooksConfig, groupID, msg, &pushToUserIDs); err != nil {
if err = p.webhookBeforeGroupOnlinePush(ctx, &p.config.WebhooksConfig.BeforeGroupOnlinePush, groupID, msg, &pushToUserIDs); err != nil {
return err return err
} }
@ -298,7 +310,8 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
// Use offline push messaging // Use offline push messaging
if len(needOfflinePushUserIDs) > 0 { if len(needOfflinePushUserIDs) > 0 {
var offlinePushUserIDs []string var offlinePushUserIDs []string
err = callbackOfflinePush(ctx, &p.config.WebhooksConfig, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
err = p.webhookBeforeOfflinePush(ctx, &p.config.WebhooksConfig.BeforeOfflinePush, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
if err != nil { if err != nil {
return err return err
} }

View File

@ -19,15 +19,19 @@ import (
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/http"
pbfriend "github.com/openimsdk/protocol/friend" pbfriend "github.com/openimsdk/protocol/friend"
"github.com/openimsdk/tools/utils/datautil"
) )
func CallbackBeforeAddFriend(ctx context.Context, callback *config.Webhooks, req *pbfriend.ApplyToAddFriendReq) error { func (s *friendServer) webhookAfterDeleteFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.DeleteFriendReq) {
if !callback.BeforeAddFriend.Enable { cbReq := &cbapi.CallbackAfterDeleteFriendReq{
return nil CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
} }
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterDeleteFriendResp{}, after)
}
func (s *friendServer) webhookBeforeAddFriend(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ApplyToAddFriendReq) error {
cbReq := &cbapi.CallbackBeforeAddFriendReq{ cbReq := &cbapi.CallbackBeforeAddFriendReq{
CallbackCommand: cbapi.CallbackBeforeAddFriendCommand, CallbackCommand: cbapi.CallbackBeforeAddFriendCommand,
FromUserID: req.FromUserID, FromUserID: req.FromUserID,
@ -36,49 +40,75 @@ func CallbackBeforeAddFriend(ctx context.Context, callback *config.Webhooks, req
Ex: req.Ex, Ex: req.Ex,
} }
resp := &cbapi.CallbackBeforeAddFriendResp{} resp := &cbapi.CallbackBeforeAddFriendResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeAddFriend); err != nil {
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
return nil return nil
} }
func CallbackBeforeSetFriendRemark(ctx context.Context, callback *config.Webhooks, req *pbfriend.SetFriendRemarkReq) error { func (s *friendServer) webhookAfterAddFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.ApplyToAddFriendReq) {
if !callback.BeforeSetFriendRemark.Enable { cbReq := &cbapi.CallbackAfterAddFriendReq{
return nil CallbackCommand: cbapi.CallbackAfterAddFriendCommand,
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
ReqMsg: req.ReqMsg,
} }
cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{ resp := &cbapi.CallbackAfterAddFriendResp{}
CallbackCommand: cbapi.CallbackBeforeSetFriendRemark, s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
Remark: req.Remark,
}
resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeAddFriend); err != nil {
return err
}
datautil.NotNilReplace(&req.Remark, &resp.Remark)
return nil
} }
func CallbackAfterSetFriendRemark(ctx context.Context, callback *config.Webhooks, req *pbfriend.SetFriendRemarkReq) error { func (s *friendServer) webhookAfterSetFriendRemark(ctx context.Context, after *config.AfterConfig, req *pbfriend.SetFriendRemarkReq) {
if !callback.AfterSetFriendRemark.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{ cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{
CallbackCommand: cbapi.CallbackAfterSetFriendRemark, CallbackCommand: cbapi.CallbackAfterSetFriendRemarkCommand,
OwnerUserID: req.OwnerUserID, OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID, FriendUserID: req.FriendUserID,
Remark: req.Remark, Remark: req.Remark,
} }
resp := &cbapi.CallbackAfterSetFriendRemarkResp{} resp := &cbapi.CallbackAfterSetFriendRemarkResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeAddFriend); err != nil { s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
func (s *friendServer) webhookAfterImportFriends(ctx context.Context, after *config.AfterConfig, req *pbfriend.ImportFriendReq) {
cbReq := &cbapi.CallbackAfterImportFriendsReq{
CallbackCommand: cbapi.CallbackAfterImportFriendsCommand,
OwnerUserID: req.OwnerUserID,
FriendUserIDs: req.FriendUserIDs,
}
resp := &cbapi.CallbackAfterImportFriendsResp{}
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
func (s *friendServer) webhookAfterRemoveBlack(ctx context.Context, after *config.AfterConfig, req *pbfriend.RemoveBlackReq) {
cbReq := &cbapi.CallbackAfterRemoveBlackReq{
CallbackCommand: cbapi.CallbackAfterRemoveBlackCommand,
OwnerUserID: req.OwnerUserID,
BlackUserID: req.BlackUserID,
}
resp := &cbapi.CallbackAfterRemoveBlackResp{}
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
func (s *friendServer) webhookBeforeSetFriendRemark(ctx context.Context, before *config.BeforeConfig, req *pbfriend.SetFriendRemarkReq) error {
cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{
CallbackCommand: cbapi.CallbackBeforeSetFriendRemarkCommand,
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
Remark: req.Remark,
}
resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
if resp.Remark != "" {
req.Remark = resp.Remark
}
return nil return nil
} }
func CallbackBeforeAddBlack(ctx context.Context, callback *config.Webhooks, req *pbfriend.AddBlackReq) error { func (s *friendServer) webhookBeforeAddBlack(ctx context.Context, before *config.BeforeConfig, req *pbfriend.AddBlackReq) error {
if !callback.BeforeAddBlack.Enable { if !before.Enable {
return nil return nil
} }
cbReq := &cbapi.CallbackBeforeAddBlackReq{ cbReq := &cbapi.CallbackBeforeAddBlackReq{
@ -87,32 +117,11 @@ func CallbackBeforeAddBlack(ctx context.Context, callback *config.Webhooks, req
BlackUserID: req.BlackUserID, BlackUserID: req.BlackUserID,
} }
resp := &cbapi.CallbackBeforeAddBlackResp{} resp := &cbapi.CallbackBeforeAddBlackResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeAddBlack); err != nil { return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
return err
}
return nil
} }
func CallbackAfterAddFriend(ctx context.Context, callback *config.Webhooks, req *pbfriend.ApplyToAddFriendReq) error { func (s *friendServer) webhookBeforeAddFriendAgree(ctx context.Context, before *config.BeforeConfig, req *pbfriend.RespondFriendApplyReq) error {
if !callback.AfterAddFriend.Enable { if !before.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterAddFriendReq{
CallbackCommand: cbapi.CallbackAfterAddFriendCommand,
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
ReqMsg: req.ReqMsg,
}
resp := &cbapi.CallbackAfterAddFriendResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterAddFriend); err != nil {
return err
}
return nil
}
func CallbackBeforeAddFriendAgree(ctx context.Context, callback *config.Webhooks, req *pbfriend.RespondFriendApplyReq) error {
if !callback.BeforeAddFriendAgree.Enable {
return nil return nil
} }
cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{ cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{
@ -123,30 +132,11 @@ func CallbackBeforeAddFriendAgree(ctx context.Context, callback *config.Webhooks
HandleResult: req.HandleResult, HandleResult: req.HandleResult,
} }
resp := &cbapi.CallbackBeforeAddFriendAgreeResp{} resp := &cbapi.CallbackBeforeAddFriendAgreeResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeAddFriendAgree); err != nil { return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
return err
}
return nil
} }
func CallbackAfterDeleteFriend(ctx context.Context, callback *config.Webhooks, req *pbfriend.DeleteFriendReq) error { func (s *friendServer) webhookBeforeImportFriends(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ImportFriendReq) error {
if !callback.AfterDeleteFriend.Enable { if !before.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterDeleteFriendReq{
CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
}
resp := &cbapi.CallbackAfterDeleteFriendResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterDeleteFriend); err != nil {
return err
}
return nil
}
func CallbackBeforeImportFriends(ctx context.Context, callback *config.Webhooks, req *pbfriend.ImportFriendReq) error {
if !callback.BeforeImportFriends.Enable {
return nil return nil
} }
cbReq := &cbapi.CallbackBeforeImportFriendsReq{ cbReq := &cbapi.CallbackBeforeImportFriendsReq{
@ -155,43 +145,11 @@ func CallbackBeforeImportFriends(ctx context.Context, callback *config.Webhooks,
FriendUserIDs: req.FriendUserIDs, FriendUserIDs: req.FriendUserIDs,
} }
resp := &cbapi.CallbackBeforeImportFriendsResp{} resp := &cbapi.CallbackBeforeImportFriendsResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeImportFriends); err != nil { if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
if len(resp.FriendUserIDs) != 0 { if len(resp.FriendUserIDs) > 0 {
req.FriendUserIDs = resp.FriendUserIDs req.FriendUserIDs = resp.FriendUserIDs
} }
return nil return nil
} }
func CallbackAfterImportFriends(ctx context.Context, callback *config.Webhooks, req *pbfriend.ImportFriendReq) error {
if !callback.AfterImportFriends.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterImportFriendsReq{
CallbackCommand: cbapi.CallbackAfterImportFriendsCommand,
OwnerUserID: req.OwnerUserID,
FriendUserIDs: req.FriendUserIDs,
}
resp := &cbapi.CallbackAfterImportFriendsResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterImportFriends); err != nil {
return err
}
return nil
}
func CallbackAfterRemoveBlack(ctx context.Context, callback *config.Webhooks, req *pbfriend.RemoveBlackReq) error {
if !callback.AfterRemoveBlack.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterRemoveBlackReq{
CallbackCommand: cbapi.CallbackAfterRemoveBlackCommand,
OwnerUserID: req.OwnerUserID,
BlackUserID: req.BlackUserID,
}
resp := &cbapi.CallbackAfterRemoveBlackResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterRemoveBlack); err != nil {
return err
}
return nil
}

View File

@ -17,6 +17,8 @@ package friend
import ( import (
"context" "context"
"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/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/authverify"
@ -38,6 +40,11 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type friendServer struct { type friendServer struct {
friendDatabase controller.FriendDatabase friendDatabase controller.FriendDatabase
blackDatabase controller.BlackDatabase blackDatabase controller.BlackDatabase
@ -46,6 +53,7 @@ type friendServer struct {
conversationRpcClient rpcclient.ConversationRpcClient conversationRpcClient rpcclient.ConversationRpcClient
RegisterCenter discovery.SvcDiscoveryRegistry RegisterCenter discovery.SvcDiscoveryRegistry
config *Config config *Config
webhookClient *webhook.Client
} }
type Config struct { type Config struct {
@ -113,6 +121,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
RegisterCenter: client, RegisterCenter: client,
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, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)),
}) })
return nil return nil
@ -124,14 +133,12 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply
if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil { if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err return nil, err
} }
if req.ToUserID == req.FromUserID { if req.ToUserID == req.FromUserID {
return nil, servererrs.ErrCanNotAddYourself.WrapMsg("req.ToUserID", req.ToUserID) return nil, servererrs.ErrCanNotAddYourself.WrapMsg("req.ToUserID", req.ToUserID)
} }
if err = CallbackBeforeAddFriend(ctx, &s.config.WebhooksConfig, req); err != nil && err != servererrs.ErrCallbackContinue { if err = s.webhookBeforeAddFriend(ctx, &s.config.WebhooksConfig.BeforeAddFriend, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil {
return nil, err return nil, err
} }
@ -140,22 +147,17 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply
if err != nil { if err != nil {
return nil, err return nil, err
} }
if in1 && in2 { if in1 && in2 {
return nil, servererrs.ErrRelationshipAlready.WrapMsg("already friends has f") return nil, servererrs.ErrRelationshipAlready.WrapMsg("already friends has f")
} }
if err = s.friendDatabase.AddFriendRequest(ctx, req.FromUserID, req.ToUserID, req.ReqMsg, req.Ex); err != nil { if err = s.friendDatabase.AddFriendRequest(ctx, req.FromUserID, req.ToUserID, req.ReqMsg, req.Ex); err != nil {
return nil, err return nil, err
} }
if err = s.notificationSender.FriendApplicationAddNotification(ctx, req); err != nil { if err = s.notificationSender.FriendApplicationAddNotification(ctx, req); err != nil {
return nil, err return nil, err
} }
if err = CallbackAfterAddFriend(ctx, &s.config.WebhooksConfig, req); err != nil && err != servererrs.ErrCallbackContinue { s.webhookAfterAddFriend(ctx, &s.config.WebhooksConfig.AfterAddFriend, req)
return nil, err
}
return resp, nil return resp, nil
} }
@ -174,7 +176,7 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr
return nil, errs.ErrArgs.WrapMsg("friend userID repeated") return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
} }
if err := CallbackBeforeImportFriends(ctx, &s.config.WebhooksConfig, req); err != nil { if err := s.webhookBeforeImportFriends(ctx, &s.config.WebhooksConfig.BeforeImportFriends, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
@ -188,9 +190,8 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr
HandleResult: constant.FriendResponseAgree, HandleResult: constant.FriendResponseAgree,
}) })
} }
if err := CallbackAfterImportFriends(ctx, &s.config.WebhooksConfig, req); err != nil {
return nil, err s.webhookAfterImportFriends(ctx, &s.config.WebhooksConfig.AfterImportFriends, req)
}
return &pbfriend.ImportFriendResp{}, nil return &pbfriend.ImportFriendResp{}, nil
} }
@ -208,7 +209,7 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res
HandleResult: req.HandleResult, HandleResult: req.HandleResult,
} }
if req.HandleResult == constant.FriendResponseAgree { if req.HandleResult == constant.FriendResponseAgree {
if err := CallbackBeforeAddFriendAgree(ctx, &s.config.WebhooksConfig, req); err != nil && err != servererrs.ErrCallbackContinue { if err := s.webhookBeforeAddFriendAgree(ctx, &s.config.WebhooksConfig.BeforeAddFriendAgree, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest) err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest)
@ -245,16 +246,13 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri
return nil, err return nil, err
} }
s.notificationSender.FriendDeletedNotification(ctx, req) s.notificationSender.FriendDeletedNotification(ctx, req)
if err := CallbackAfterDeleteFriend(ctx, &s.config.WebhooksConfig, req); err != nil { s.webhookAfterDeleteFriend(ctx, &s.config.WebhooksConfig.AfterDeleteFriend, req)
return nil, err
}
return resp, nil return resp, nil
} }
// ok. // ok.
func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) { func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) {
if err = s.webhookBeforeSetFriendRemark(ctx, &s.config.WebhooksConfig.BeforeSetFriendRemark, req); err != nil && err != servererrs.ErrCallbackContinue {
if err = CallbackBeforeSetFriendRemark(ctx, &s.config.WebhooksConfig, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
resp = &pbfriend.SetFriendRemarkResp{} resp = &pbfriend.SetFriendRemarkResp{}
@ -268,9 +266,7 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri
if err := s.friendDatabase.UpdateRemark(ctx, req.OwnerUserID, req.FriendUserID, req.Remark); err != nil { if err := s.friendDatabase.UpdateRemark(ctx, req.OwnerUserID, req.FriendUserID, req.Remark); err != nil {
return nil, err return nil, err
} }
if err := CallbackAfterSetFriendRemark(ctx, &s.config.WebhooksConfig, req); err != nil && err != servererrs.ErrCallbackContinue { s.webhookAfterSetFriendRemark(ctx, &s.config.WebhooksConfig.AfterSetFriendRemark, req)
return nil, err
}
s.notificationSender.FriendRemarkSetNotification(ctx, req.OwnerUserID, req.FriendUserID) s.notificationSender.FriendRemarkSetNotification(ctx, req.OwnerUserID, req.FriendUserID)
return resp, nil return resp, nil
} }

View File

@ -16,32 +16,21 @@ package group
import ( import (
"context" "context"
"github.com/openimsdk/tools/utils/datautil"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/apistruct"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/group" "github.com/openimsdk/protocol/group"
pbgroup "github.com/openimsdk/protocol/group"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/utils/datautil"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
"time"
) )
type GroupEventCallbackConfig struct {
CallbackUrl string
BeforeCreateGroup config.WebhookConfig
}
// CallbackBeforeCreateGroup callback before create group. // CallbackBeforeCreateGroup callback before create group.
func CallbackBeforeCreateGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.CreateGroupReq) error { func (s *groupServer) webhookBeforeCreateGroup(ctx context.Context, before *config.BeforeConfig, req *group.CreateGroupReq) error {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{ cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{
CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand, CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
@ -65,9 +54,10 @@ func CallbackBeforeCreateGroup(ctx context.Context, cfg *GroupEventCallbackConfi
} }
resp := &callbackstruct.CallbackBeforeCreateGroupResp{} resp := &callbackstruct.CallbackBeforeCreateGroupResp{}
if err := http.CallBackPostReturn(ctx, cfg.CallbackUrl, cbReq, resp, cfg.BeforeCreateGroup); err != nil { if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
datautil.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID) datautil.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID)
datautil.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName) datautil.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName)
datautil.NotNilReplace(&req.GroupInfo.Notification, resp.Notification) datautil.NotNilReplace(&req.GroupInfo.Notification, resp.Notification)
@ -83,11 +73,7 @@ func CallbackBeforeCreateGroup(ctx context.Context, cfg *GroupEventCallbackConfi
return nil return nil
} }
func CallbackAfterCreateGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.CreateGroupReq) error { func (s *groupServer) webhookAfterCreateGroup(ctx context.Context, after *config.AfterConfig, req *group.CreateGroupReq) {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
cbReq := &callbackstruct.CallbackAfterCreateGroupReq{ cbReq := &callbackstruct.CallbackAfterCreateGroupReq{
CallbackCommand: callbackstruct.CallbackAfterCreateGroupCommand, CallbackCommand: callbackstruct.CallbackAfterCreateGroupCommand,
GroupInfo: req.GroupInfo, GroupInfo: req.GroupInfo,
@ -108,15 +94,11 @@ func CallbackAfterCreateGroup(ctx context.Context, cfg *GroupEventCallbackConfig
RoleLevel: constant.GroupOrdinaryUsers, RoleLevel: constant.GroupOrdinaryUsers,
}) })
} }
resp := &callbackstruct.CallbackAfterCreateGroupResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateGroupResp{}, after)
return http.CallBackPostReturn(ctx, cfg.CallbackUrl, cbReq, resp, cfg.BeforeCreateGroup)
} }
func CallbackBeforeMemberJoinGroup(ctx context.Context, cfg *GroupEventCallbackConfig, groupMember *relation.GroupMemberModel, groupEx string) error { func (s *groupServer) webhookBeforeMemberJoinGroup(ctx context.Context, before *config.BeforeConfig, groupMember *relation.GroupMemberModel, groupEx string) error {
if !cfg.BeforeCreateGroup.Enable { cbReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
return nil
}
callbackReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand, CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand,
GroupID: groupMember.GroupID, GroupID: groupMember.GroupID,
UserID: groupMember.UserID, UserID: groupMember.UserID,
@ -124,15 +106,13 @@ func CallbackBeforeMemberJoinGroup(ctx context.Context, cfg *GroupEventCallbackC
GroupEx: groupEx, GroupEx: groupEx,
} }
resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{} resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{}
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
if err := http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup); err != nil {
return err return err
} }
if resp.MuteEndTime != nil { if resp.MuteEndTime != nil {
groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime) groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime)
} }
datautil.NotNilReplace(&groupMember.FaceURL, resp.FaceURL) datautil.NotNilReplace(&groupMember.FaceURL, resp.FaceURL)
datautil.NotNilReplace(&groupMember.Ex, resp.Ex) datautil.NotNilReplace(&groupMember.Ex, resp.Ex)
datautil.NotNilReplace(&groupMember.Nickname, resp.Nickname) datautil.NotNilReplace(&groupMember.Nickname, resp.Nickname)
@ -140,31 +120,26 @@ func CallbackBeforeMemberJoinGroup(ctx context.Context, cfg *GroupEventCallbackC
return nil return nil
} }
func CallbackBeforeSetGroupMemberInfo(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.SetGroupMemberInfo) error { func (s *groupServer) webhookBeforeSetGroupMemberInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupMemberInfo) error {
if !cfg.BeforeCreateGroup.Enable { cbReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
return nil
}
callbackReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand, CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand,
GroupID: req.GroupID, GroupID: req.GroupID,
UserID: req.UserID, UserID: req.UserID,
} }
if req.Nickname != nil { if req.Nickname != nil {
callbackReq.Nickname = &req.Nickname.Value cbReq.Nickname = &req.Nickname.Value
} }
if req.FaceURL != nil { if req.FaceURL != nil {
callbackReq.FaceURL = &req.FaceURL.Value cbReq.FaceURL = &req.FaceURL.Value
} }
if req.RoleLevel != nil { if req.RoleLevel != nil {
callbackReq.RoleLevel = &req.RoleLevel.Value cbReq.RoleLevel = &req.RoleLevel.Value
} }
if req.Ex != nil { if req.Ex != nil {
callbackReq.Ex = &req.Ex.Value cbReq.Ex = &req.Ex.Value
} }
resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{} resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{}
err := http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup) if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
if err != nil {
return err return err
} }
if resp.FaceURL != nil { if resp.FaceURL != nil {
@ -182,112 +157,71 @@ func CallbackBeforeSetGroupMemberInfo(ctx context.Context, cfg *GroupEventCallba
return nil return nil
} }
func CallbackAfterSetGroupMemberInfo(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.SetGroupMemberInfo) error { func (s *groupServer) webhookAfterSetGroupMemberInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupMemberInfo) {
if !cfg.BeforeCreateGroup.Enable { cbReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
return nil
}
callbackReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
CallbackCommand: callbackstruct.CallbackAfterSetGroupMemberInfoCommand, CallbackCommand: callbackstruct.CallbackAfterSetGroupMemberInfoCommand,
GroupID: req.GroupID, GroupID: req.GroupID,
UserID: req.UserID, UserID: req.UserID,
} }
if req.Nickname != nil { if req.Nickname != nil {
callbackReq.Nickname = &req.Nickname.Value cbReq.Nickname = &req.Nickname.Value
} }
if req.FaceURL != nil { if req.FaceURL != nil {
callbackReq.FaceURL = &req.FaceURL.Value cbReq.FaceURL = &req.FaceURL.Value
} }
if req.RoleLevel != nil { if req.RoleLevel != nil {
callbackReq.RoleLevel = &req.RoleLevel.Value cbReq.RoleLevel = &req.RoleLevel.Value
} }
if req.Ex != nil { if req.Ex != nil {
callbackReq.Ex = &req.Ex.Value cbReq.Ex = &req.Ex.Value
} }
resp := &callbackstruct.CallbackAfterSetGroupMemberInfoResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupMemberInfoResp{}, after)
return http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup)
} }
func CallbackQuitGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.QuitGroupReq) error { func (s *groupServer) webhookAfterQuitGroup(ctx context.Context, after *config.AfterConfig, req *group.QuitGroupReq) {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
cbReq := &callbackstruct.CallbackQuitGroupReq{ cbReq := &callbackstruct.CallbackQuitGroupReq{
CallbackCommand: callbackstruct.CallbackQuitGroupCommand, CallbackCommand: callbackstruct.CallbackAfterQuitGroupCommand,
GroupID: req.GroupID, GroupID: req.GroupID,
UserID: req.UserID, UserID: req.UserID,
} }
resp := &callbackstruct.CallbackQuitGroupResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackQuitGroupResp{}, after)
return http.CallBackPostReturn(ctx, cfg.CallbackUrl, cbReq, resp, cfg.BeforeCreateGroup)
} }
func CallbackKillGroupMember(ctx context.Context, cfg *GroupEventCallbackConfig, req *pbgroup.KickGroupMemberReq) (err error) { func (s *groupServer) webhookAfterKickGroupMember(ctx context.Context, after *config.AfterConfig, req *group.KickGroupMemberReq) {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
cbReq := &callbackstruct.CallbackKillGroupMemberReq{ cbReq := &callbackstruct.CallbackKillGroupMemberReq{
CallbackCommand: callbackstruct.CallbackKillGroupCommand, CallbackCommand: callbackstruct.CallbackAfterKickGroupCommand,
GroupID: req.GroupID, GroupID: req.GroupID,
KickedUserIDs: req.KickedUserIDs, KickedUserIDs: req.KickedUserIDs,
} }
resp := &callbackstruct.CallbackKillGroupMemberResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackKillGroupMemberResp{}, after)
if err = http.CallBackPostReturn(ctx, cfg.CallbackUrl, cbReq, resp, cfg.BeforeCreateGroup); err != nil {
return err
}
return nil
} }
func CallbackDismissGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *callbackstruct.CallbackDisMissGroupReq) (err error) { func (s *groupServer) webhookAfterDismissGroup(ctx context.Context, after *config.AfterConfig, req *callbackstruct.CallbackDisMissGroupReq) {
if !cfg.BeforeCreateGroup.Enable { req.CallbackCommand = callbackstruct.CallbackAfterDisMissGroupCommand
return nil s.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &callbackstruct.CallbackDisMissGroupResp{}, after)
}
req.CallbackCommand = callbackstruct.CallbackDisMissGroupCommand
resp := &callbackstruct.CallbackDisMissGroupResp{}
if err = http.CallBackPostReturn(ctx, cfg.CallbackUrl, req, resp, cfg.BeforeCreateGroup); err != nil {
return err
}
return nil
} }
func CallbackApplyJoinGroupBefore(ctx context.Context, cfg *GroupEventCallbackConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) { func (s *groupServer) webhookBeforeApplyJoinGroup(ctx context.Context, before *config.BeforeConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand
resp := &callbackstruct.CallbackJoinGroupResp{} resp := &callbackstruct.CallbackJoinGroupResp{}
if err = http.CallBackPostReturn(ctx, cfg.CallbackUrl, req, resp, cfg.BeforeCreateGroup); err != nil { if err := s.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
return err return err
} }
return nil return nil
} }
func CallbackAfterTransferGroupOwner(ctx context.Context, cfg *GroupEventCallbackConfig, req *pbgroup.TransferGroupOwnerReq) (err error) { func (s *groupServer) webhookAfterTransferGroupOwner(ctx context.Context, after *config.AfterConfig, req *group.TransferGroupOwnerReq) {
if !cfg.BeforeCreateGroup.Enable {
return nil
}
cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{ cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{
CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwner, CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwnerCommand,
GroupID: req.GroupID, GroupID: req.GroupID,
OldOwnerUserID: req.OldOwnerUserID, OldOwnerUserID: req.OldOwnerUserID,
NewOwnerUserID: req.NewOwnerUserID, NewOwnerUserID: req.NewOwnerUserID,
} }
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackTransferGroupOwnerResp{}, after)
resp := &callbackstruct.CallbackTransferGroupOwnerResp{}
if err = http.CallBackPostReturn(ctx, cfg.CallbackUrl, cbReq, resp, cfg.BeforeCreateGroup); err != nil {
return err
}
return nil
} }
func CallbackBeforeInviteUserToGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.InviteUserToGroupReq) (err error) { func (s *groupServer) webhookBeforeInviteUserToGroup(ctx context.Context, before *config.BeforeConfig, req *group.InviteUserToGroupReq) (err error) {
if !cfg.BeforeCreateGroup.Enable { cbReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
return nil
}
callbackReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand, CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
GroupID: req.GroupID, GroupID: req.GroupID,
@ -296,15 +230,7 @@ func CallbackBeforeInviteUserToGroup(ctx context.Context, cfg *GroupEventCallbac
} }
resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{} resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{}
err = http.CallBackPostReturn( if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
ctx,
cfg.CallbackUrl,
callbackReq,
resp,
cfg.BeforeCreateGroup,
)
if err != nil {
return err return err
} }
@ -315,11 +241,8 @@ func CallbackBeforeInviteUserToGroup(ctx context.Context, cfg *GroupEventCallbac
return nil return nil
} }
func CallbackAfterJoinGroup(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.JoinGroupReq) error { func (s *groupServer) webhookAfterJoinGroup(ctx context.Context, after *config.AfterConfig, req *group.JoinGroupReq) {
if !cfg.BeforeCreateGroup.Enable { cbReq := &callbackstruct.CallbackAfterJoinGroupReq{
return nil
}
callbackReq := &callbackstruct.CallbackAfterJoinGroupReq{
CallbackCommand: callbackstruct.CallbackAfterJoinGroupCommand, CallbackCommand: callbackstruct.CallbackAfterJoinGroupCommand,
OperationID: mcontext.GetOperationID(ctx), OperationID: mcontext.GetOperationID(ctx),
GroupID: req.GroupID, GroupID: req.GroupID,
@ -327,18 +250,12 @@ func CallbackAfterJoinGroup(ctx context.Context, cfg *GroupEventCallbackConfig,
JoinSource: req.JoinSource, JoinSource: req.JoinSource,
InviterUserID: req.InviterUserID, InviterUserID: req.InviterUserID,
} }
resp := &callbackstruct.CallbackAfterJoinGroupResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterJoinGroupResp{}, after)
if err := http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup); err != nil {
return err
}
return nil
} }
func CallbackBeforeSetGroupInfo(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.SetGroupInfoReq) error { func (s *groupServer) webhookBeforeSetGroupInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupInfoReq) error {
if !cfg.BeforeCreateGroup.Enable {
return nil cbReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
}
callbackReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand, CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand,
GroupID: req.GroupInfoForSet.GroupID, GroupID: req.GroupInfoForSet.GroupID,
Notification: req.GroupInfoForSet.Notification, Notification: req.GroupInfoForSet.Notification,
@ -346,23 +263,22 @@ func CallbackBeforeSetGroupInfo(ctx context.Context, cfg *GroupEventCallbackConf
FaceURL: req.GroupInfoForSet.FaceURL, FaceURL: req.GroupInfoForSet.FaceURL,
GroupName: req.GroupInfoForSet.GroupName, GroupName: req.GroupInfoForSet.GroupName,
} }
if req.GroupInfoForSet.Ex != nil { if req.GroupInfoForSet.Ex != nil {
callbackReq.Ex = req.GroupInfoForSet.Ex.Value cbReq.Ex = req.GroupInfoForSet.Ex.Value
} }
log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", callbackReq.Ex) log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", cbReq.Ex)
if req.GroupInfoForSet.NeedVerification != nil { if req.GroupInfoForSet.NeedVerification != nil {
callbackReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value cbReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value
} }
if req.GroupInfoForSet.LookMemberInfo != nil { if req.GroupInfoForSet.LookMemberInfo != nil {
callbackReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value cbReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value
} }
if req.GroupInfoForSet.ApplyMemberFriend != nil { if req.GroupInfoForSet.ApplyMemberFriend != nil {
callbackReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value cbReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value
} }
resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{} resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{}
if err := http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup); err != nil { if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
@ -385,11 +301,8 @@ func CallbackBeforeSetGroupInfo(ctx context.Context, cfg *GroupEventCallbackConf
return nil return nil
} }
func CallbackAfterSetGroupInfo(ctx context.Context, cfg *GroupEventCallbackConfig, req *group.SetGroupInfoReq) error { func (s *groupServer) webhookAfterSetGroupInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupInfoReq) {
if !cfg.BeforeCreateGroup.Enable { cbReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
return nil
}
callbackReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
CallbackCommand: callbackstruct.CallbackAfterSetGroupInfoCommand, CallbackCommand: callbackstruct.CallbackAfterSetGroupInfoCommand,
GroupID: req.GroupInfoForSet.GroupID, GroupID: req.GroupInfoForSet.GroupID,
Notification: req.GroupInfoForSet.Notification, Notification: req.GroupInfoForSet.Notification,
@ -398,17 +311,16 @@ func CallbackAfterSetGroupInfo(ctx context.Context, cfg *GroupEventCallbackConfi
GroupName: req.GroupInfoForSet.GroupName, GroupName: req.GroupInfoForSet.GroupName,
} }
if req.GroupInfoForSet.Ex != nil { if req.GroupInfoForSet.Ex != nil {
callbackReq.Ex = &req.GroupInfoForSet.Ex.Value cbReq.Ex = &req.GroupInfoForSet.Ex.Value
} }
if req.GroupInfoForSet.NeedVerification != nil { if req.GroupInfoForSet.NeedVerification != nil {
callbackReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value cbReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value
} }
if req.GroupInfoForSet.LookMemberInfo != nil { if req.GroupInfoForSet.LookMemberInfo != nil {
callbackReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value cbReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value
} }
if req.GroupInfoForSet.ApplyMemberFriend != nil { if req.GroupInfoForSet.ApplyMemberFriend != nil {
callbackReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value cbReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value
} }
resp := &callbackstruct.CallbackAfterSetGroupInfoResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupInfoResp{}, after)
return http.CallBackPostReturn(ctx, cfg.CallbackUrl, callbackReq, resp, cfg.BeforeCreateGroup)
} }

View File

@ -19,6 +19,8 @@ import (
"fmt" "fmt"
"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/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"math/big" "math/big"
"math/rand" "math/rand"
"strconv" "strconv"
@ -53,6 +55,11 @@ import (
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
) )
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type groupServer struct { type groupServer struct {
db controller.GroupDatabase db controller.GroupDatabase
user rpcclient.UserRpcClient user rpcclient.UserRpcClient
@ -60,6 +67,7 @@ type groupServer struct {
conversationRpcClient rpcclient.ConversationRpcClient conversationRpcClient rpcclient.ConversationRpcClient
msgRpcClient rpcclient.MessageRpcClient msgRpcClient rpcclient.MessageRpcClient
config *Config config *Config
webhookClient *webhook.Client
} }
type Config struct { type Config struct {
@ -112,6 +120,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
gs.conversationRpcClient = conversationRpcClient gs.conversationRpcClient = conversationRpcClient
gs.msgRpcClient = msgRpcClient gs.msgRpcClient = msgRpcClient
gs.config = config gs.config = config
gs.webhookClient = webhook.NewWebhookClient(config.WebhooksConfig.URL, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize))
pbgroup.RegisterGroupServer(server, &gs) pbgroup.RegisterGroupServer(server, &gs)
return nil return nil
} }
@ -229,13 +238,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
return nil, servererrs.ErrUserIDNotFound.WrapMsg("user not found") return nil, servererrs.ErrUserIDNotFound.WrapMsg("user not found")
} }
config := &GroupEventCallbackConfig{ if err := s.webhookBeforeCreateGroup(ctx, &s.config.WebhooksConfig.BeforeCreateGroup, req); err != nil && err != servererrs.ErrCallbackContinue {
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeCreateGroup,
}
// Callback Before create Group
if err := CallbackBeforeCreateGroup(ctx, config, req); err != nil {
return nil, err return nil, err
} }
@ -245,11 +248,6 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
return nil, err return nil, err
} }
beforeCreateGroupConfig := &GroupEventCallbackConfig{
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
joinGroup := func(userID string, roleLevel int32) error { joinGroup := func(userID string, roleLevel int32) error {
groupMember := &relationtb.GroupMemberModel{ groupMember := &relationtb.GroupMemberModel{
GroupID: group.GroupID, GroupID: group.GroupID,
@ -262,7 +260,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
MuteEndTime: time.UnixMilli(0), MuteEndTime: time.UnixMilli(0),
} }
if err := CallbackBeforeMemberJoinGroup(ctx, beforeCreateGroupConfig, groupMember, group.Ex); err != nil { if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return err return err
} }
groupMembers = append(groupMembers, groupMember) groupMembers = append(groupMembers, groupMember)
@ -331,9 +329,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
AdminUserIDs: req.AdminUserIDs, AdminUserIDs: req.AdminUserIDs,
} }
if err := CallbackAfterCreateGroup(ctx, beforeCreateGroupConfig, reqCallBackAfter); err != nil { s.webhookAfterCreateGroup(ctx, &s.config.WebhooksConfig.AfterCreateGroup, reqCallBackAfter)
return nil, err
}
return resp, nil return resp, nil
} }
@ -423,12 +419,7 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
} }
} }
beforeInviteUserToGroupConfig := &GroupEventCallbackConfig{ if err := s.webhookBeforeInviteUserToGroup(ctx, &s.config.WebhooksConfig.BeforeInviteUserToGroup, req); err != nil && err != servererrs.ErrCallbackContinue {
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeInviteUserToGroup,
}
if err := CallbackBeforeInviteUserToGroup(ctx, beforeInviteUserToGroupConfig, req); err != nil {
return nil, err return nil, err
} }
@ -474,12 +465,11 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
MuteEndTime: time.UnixMilli(0), MuteEndTime: time.UnixMilli(0),
} }
beforeMemberJoinGroupConfig := beforeInviteUserToGroupConfig if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
if err := CallbackBeforeMemberJoinGroup(ctx, beforeMemberJoinGroupConfig, member, group.Ex); err != nil {
return nil, err return nil, err
} }
groupMembers = append(groupMembers, member) groupMembers = append(groupMembers, member)
} }
if err := s.db.CreateGroup(ctx, nil, groupMembers); err != nil { if err := s.db.CreateGroup(ctx, nil, groupMembers); err != nil {
return nil, err return nil, err
@ -647,15 +637,8 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou
if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, req.KickedUserIDs); err != nil { if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, req.KickedUserIDs); err != nil {
return nil, err return nil, err
} }
s.webhookAfterKickGroupMember(ctx, &s.config.WebhooksConfig.AfterKickGroupMember, req)
killGroupMemberConfig := &GroupEventCallbackConfig{
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err := CallbackKillGroupMember(ctx, killGroupMemberConfig, req); err != nil {
return nil, err
}
return &pbgroup.KickGroupMemberResp{}, nil return &pbgroup.KickGroupMemberResp{}, nil
} }
@ -823,13 +806,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
OperatorUserID: mcontext.GetOpUserID(ctx), OperatorUserID: mcontext.GetOpUserID(ctx),
Ex: groupRequest.Ex, Ex: groupRequest.Ex,
} }
if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, member, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
beforeMemberJoinGroupConfig := &GroupEventCallbackConfig{
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err = CallbackBeforeMemberJoinGroup(ctx, beforeMemberJoinGroupConfig, member, group.Ex); err != nil {
return nil, err return nil, err
} }
} }
@ -876,14 +853,10 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
Ex: req.Ex, Ex: req.Ex,
} }
applyJoinGroupBeforeConfig := &GroupEventCallbackConfig{ if err := s.webhookBeforeApplyJoinGroup(ctx, &s.config.WebhooksConfig.BeforeApplyJoinGroup, reqCall); err != nil && err != servererrs.ErrCallbackContinue {
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err = CallbackApplyJoinGroupBefore(ctx, applyJoinGroupBeforeConfig, reqCall); err != nil {
return nil, err return nil, err
} }
_, err = s.db.TakeGroupMember(ctx, req.GroupID, req.InviterUserID) _, err = s.db.TakeGroupMember(ctx, req.GroupID, req.InviterUserID)
if err == nil { if err == nil {
return nil, errs.ErrArgs.Wrap() return nil, errs.ErrArgs.Wrap()
@ -901,10 +874,11 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
JoinTime: time.Now(), JoinTime: time.Now(),
MuteEndTime: time.UnixMilli(0), MuteEndTime: time.UnixMilli(0),
} }
MemberJoinGroupConfig := applyJoinGroupBeforeConfig
if err := CallbackBeforeMemberJoinGroup(ctx, MemberJoinGroupConfig, groupMember, group.Ex); err != nil { if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil { if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil {
return nil, err return nil, err
} }
@ -913,10 +887,8 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
return nil, err return nil, err
} }
s.notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID) s.notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID)
afterJoinGroupConfig := applyJoinGroupBeforeConfig s.webhookAfterJoinGroup(ctx, &s.config.WebhooksConfig.AfterJoinGroup, req)
if err = CallbackAfterJoinGroup(ctx, afterJoinGroupConfig, req); err != nil {
return nil, err
}
return &pbgroup.JoinGroupResp{}, nil return &pbgroup.JoinGroupResp{}, nil
} }
groupRequest := relationtb.GroupRequestModel{ groupRequest := relationtb.GroupRequestModel{
@ -961,15 +933,8 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq)
if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, []string{req.UserID}); err != nil { if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, []string{req.UserID}); err != nil {
return nil, err return nil, err
} }
s.webhookAfterQuitGroup(ctx, &s.config.WebhooksConfig.AfterQuitGroup, req)
quitGroupConfig := &GroupEventCallbackConfig{
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err := CallbackQuitGroup(ctx, quitGroupConfig, req); err != nil {
return nil, err
}
return &pbgroup.QuitGroupResp{}, nil return &pbgroup.QuitGroupResp{}, nil
} }
@ -998,14 +963,10 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
} }
} }
beforeSetGroupInfoConfig := &GroupEventCallbackConfig{ if err := s.webhookBeforeSetGroupInfo(ctx, &s.config.WebhooksConfig.BeforeSetGroupInfo, req); err != nil && err != servererrs.ErrCallbackContinue {
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err := CallbackBeforeSetGroupInfo(ctx, beforeSetGroupInfoConfig, req); err != nil {
return nil, err return nil, err
} }
group, err := s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID) group, err := s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1073,10 +1034,8 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
_ = s.notification.GroupInfoSetNotification(ctx, tips) _ = s.notification.GroupInfoSetNotification(ctx, tips)
} }
afterSetGroupInfoConfig := beforeSetGroupInfoConfig s.webhookAfterSetGroupInfo(ctx, &s.config.WebhooksConfig.AfterSetGroupInfo, req)
if err := CallbackAfterSetGroupInfo(ctx, afterSetGroupInfoConfig, req); err != nil {
return nil, err
}
return &pbgroup.SetGroupInfoResp{}, nil return &pbgroup.SetGroupInfoResp{}, nil
} }
@ -1119,14 +1078,8 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans
return nil, err return nil, err
} }
afterTransferGroupOwnerConfig := &GroupEventCallbackConfig{ s.webhookAfterTransferGroupOwner(ctx, &s.config.WebhooksConfig.AfterTransferGroupOwner, req)
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err := CallbackAfterTransferGroupOwner(ctx, afterTransferGroupOwnerConfig, req); err != nil {
return nil, err
}
s.notification.GroupOwnerTransferredNotification(ctx, req) s.notification.GroupOwnerTransferredNotification(ctx, req)
return &pbgroup.TransferGroupOwnerResp{}, nil return &pbgroup.TransferGroupOwnerResp{}, nil
} }
@ -1285,21 +1238,14 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou
if err != nil { if err != nil {
return nil, err return nil, err
} }
reqCall := &callbackstruct.CallbackDisMissGroupReq{ cbReq := &callbackstruct.CallbackDisMissGroupReq{
GroupID: req.GroupID, GroupID: req.GroupID,
OwnerID: owner.UserID, OwnerID: owner.UserID,
MembersID: membersID, MembersID: membersID,
GroupType: string(group.GroupType), GroupType: string(group.GroupType),
} }
dismissGroupConfig := &GroupEventCallbackConfig{ s.webhookAfterDismissGroup(ctx, &s.config.WebhooksConfig.AfterDismissGroup, cbReq)
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
if err := CallbackDismissGroup(ctx, dismissGroupConfig, reqCall); err != nil {
return nil, err
}
return &pbgroup.DismissGroupResp{}, nil return &pbgroup.DismissGroupResp{}, nil
} }
@ -1485,15 +1431,12 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
} }
} }
beforeSetGroupMemberInfoConfig := &GroupEventCallbackConfig{
CallbackUrl: s.config.WebhooksConfig.URL,
BeforeCreateGroup: s.config.WebhooksConfig.BeforeMemberJoinGroup,
}
for i := 0; i < len(req.Members); i++ { for i := 0; i < len(req.Members); i++ {
if err := CallbackBeforeSetGroupMemberInfo(ctx, beforeSetGroupMemberInfoConfig, req.Members[i]); err != nil {
if err := s.webhookBeforeSetGroupMemberInfo(ctx, &s.config.WebhooksConfig.BeforeSetGroupMemberInfo, req.Members[i]); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err return nil, err
} }
} }
if err := s.db.UpdateGroupMembers(ctx, datautil.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember { if err := s.db.UpdateGroupMembers(ctx, datautil.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember {
return &relationtb.BatchUpdateGroupMember{ return &relationtb.BatchUpdateGroupMember{
@ -1517,11 +1460,8 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
s.notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID) s.notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID)
} }
} }
afterSetGroupMemberInfoConfig := beforeSetGroupMemberInfoConfig
for i := 0; i < len(req.Members); i++ { for i := 0; i < len(req.Members); i++ {
if err := CallbackAfterSetGroupMemberInfo(ctx, afterSetGroupMemberInfoConfig, req.Members[i]); err != nil { s.webhookAfterSetGroupMemberInfo(ctx, &s.config.WebhooksConfig.AfterSetGroupMemberInfo, req.Members[i])
return nil, err
}
} }
return &pbgroup.SetGroupMemberInfoResp{}, nil return &pbgroup.SetGroupMemberInfoResp{}, nil

View File

@ -125,9 +125,7 @@ func (m *msgServer) MarkMsgsAsRead(ctx context.Context, req *msg.MarkMsgsAsReadR
Seqs: req.Seqs, Seqs: req.Seqs,
ContentType: conversation.ConversationType, ContentType: conversation.ConversationType,
} }
if err := CallbackSingleMsgRead(ctx, &m.config.WebhooksConfig, reqCallback); err != nil { m.webhookAfterSingleMsgRead(ctx, &m.config.WebhooksConfig.AfterSingleMsgRead, reqCallback)
return nil, err
}
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID, if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil { m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
@ -198,10 +196,8 @@ func (m *msgServer) MarkConversationAsRead(ctx context.Context, req *msg.MarkCon
UnreadMsgNum: req.HasReadSeq, UnreadMsgNum: req.HasReadSeq,
ContentType: int64(conversation.ConversationType), ContentType: int64(conversation.ConversationType),
} }
if err := CallbackGroupMsgRead(ctx, &m.config.WebhooksConfig, reqCall); err != nil {
return nil, err
}
m.webhookAfterGroupMsgRead(ctx, &m.config.WebhooksConfig.AfterGroupMsgRead, reqCall)
return &msg.MarkConversationAsReadResp{}, nil return &msg.MarkConversationAsReadResp{}, nil
} }

View File

@ -19,7 +19,6 @@ import (
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/http"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
pbchat "github.com/openimsdk/protocol/msg" pbchat "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
@ -61,77 +60,71 @@ func GetContent(msg *sdkws.MsgData) string {
} }
} }
func callbackBeforeSendSingleMsg(ctx context.Context, callback *config.Webhooks, msg *pbchat.SendMsgReq) error { func (m *msgServer) webhookBeforeSendSingleMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
if !callback.BeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { if msg.MsgData.ContentType == constant.Typing {
return nil return nil
} }
req := &cbapi.CallbackBeforeSendSingleMsgReq{ cbReq := &cbapi.CallbackBeforeSendSingleMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand), CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand),
RecvID: msg.MsgData.RecvID, RecvID: msg.MsgData.RecvID,
} }
resp := &cbapi.CallbackBeforeSendSingleMsgResp{} resp := &cbapi.CallbackBeforeSendSingleMsgResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeSendSingleMsg); err != nil { if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
return nil return nil
} }
func callbackAfterSendSingleMsg(ctx context.Context, callback *config.Webhooks, msg *pbchat.SendMsgReq) error { func (m *msgServer) webhookAfterSendSingleMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
if !callback.AfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { if msg.MsgData.ContentType == constant.Typing {
return nil return
} }
req := &cbapi.CallbackAfterSendSingleMsgReq{ cbReq := &cbapi.CallbackAfterSendSingleMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendSingleMsgCommand), CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendSingleMsgCommand),
RecvID: msg.MsgData.RecvID, RecvID: msg.MsgData.RecvID,
} }
resp := &cbapi.CallbackAfterSendSingleMsgResp{} m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendSingleMsgResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterSendSingleMsg); err != nil {
return err
}
return nil
} }
func callbackBeforeSendGroupMsg(ctx context.Context, callback *config.Webhooks, msg *pbchat.SendMsgReq) error { func (m *msgServer) webhookBeforeSendGroupMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
if !callback.BeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { if msg.MsgData.ContentType == constant.Typing {
return nil return nil
} }
req := &cbapi.CallbackBeforeSendGroupMsgReq{ cbReq := &cbapi.CallbackBeforeSendGroupMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand), CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand),
GroupID: msg.MsgData.GroupID, GroupID: msg.MsgData.GroupID,
} }
resp := &cbapi.CallbackBeforeSendGroupMsgResp{} resp := &cbapi.CallbackBeforeSendGroupMsgResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeSendGroupMsg); err != nil { if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
return nil return nil
} }
func callbackAfterSendGroupMsg(ctx context.Context, callback *config.Webhooks, msg *pbchat.SendMsgReq) error { func (m *msgServer) webhookAfterSendGroupMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
if !callback.AfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { if msg.MsgData.ContentType == constant.Typing {
return nil return
} }
req := &cbapi.CallbackAfterSendGroupMsgReq{ cbReq := &cbapi.CallbackAfterSendGroupMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendGroupMsgCommand), CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendGroupMsgCommand),
GroupID: msg.MsgData.GroupID, GroupID: msg.MsgData.GroupID,
} }
resp := &cbapi.CallbackAfterSendGroupMsgResp{} m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendGroupMsgResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterSendGroupMsg); err != nil {
return err
}
return nil
} }
func callbackMsgModify(ctx context.Context, callback *config.Webhooks, msg *pbchat.SendMsgReq) error { func (m *msgServer) webhookBeforeMsgModify(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
if !callback.BeforeMsgModify.Enable || msg.MsgData.ContentType != constant.Text { if msg.MsgData.ContentType != constant.Text {
return nil return nil
} }
req := &cbapi.CallbackMsgModifyCommandReq{ cbReq := &cbapi.CallbackMsgModifyCommandReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackMsgModifyCommand), CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeMsgModifyCommand),
} }
resp := &cbapi.CallbackMsgModifyCommandResp{} resp := &cbapi.CallbackMsgModifyCommandResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.BeforeMsgModify); err != nil { if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
if resp.Content != nil { if resp.Content != nil {
msg.MsgData.Content = []byte(*resp.Content) msg.MsgData.Content = []byte(*resp.Content)
} }
@ -154,45 +147,25 @@ func callbackMsgModify(ctx context.Context, callback *config.Webhooks, msg *pbch
return nil return nil
} }
func CallbackGroupMsgRead(ctx context.Context, callback *config.Webhooks, req *cbapi.CallbackGroupMsgReadReq) error { func (m *msgServer) webhookAfterGroupMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackGroupMsgReadReq) {
if !callback.AfterGroupMsgRead.Enable { req.CallbackCommand = cbapi.CallbackAfterGroupMsgReadCommand
return nil m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackGroupMsgReadResp{}, after)
}
req.CallbackCommand = cbapi.CallbackGroupMsgReadCommand
resp := &cbapi.CallbackGroupMsgReadResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterGroupMsgRead); err != nil {
return err
}
return nil
} }
func CallbackSingleMsgRead(ctx context.Context, callback *config.Webhooks, req *cbapi.CallbackSingleMsgReadReq) error { func (m *msgServer) webhookAfterSingleMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackSingleMsgReadReq) {
if !callback.AfterSingleMsgRead.Enable {
return nil
}
req.CallbackCommand = cbapi.CallbackSingleMsgRead
resp := &cbapi.CallbackSingleMsgReadResp{} req.CallbackCommand = cbapi.CallbackAfterSingleMsgReadCommand
m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackSingleMsgReadResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, req, resp, callback.AfterSingleMsgRead); err != nil {
return err
}
return nil
} }
func CallbackAfterRevokeMsg(ctx context.Context, callback *config.Webhooks, req *pbchat.RevokeMsgReq) error {
if !callback.AfterRevokeMsg.Enable { func (m *msgServer) webhookAfterRevokeMsg(ctx context.Context, after *config.AfterConfig, req *pbchat.RevokeMsgReq) {
return nil
}
callbackReq := &cbapi.CallbackAfterRevokeMsgReq{ callbackReq := &cbapi.CallbackAfterRevokeMsgReq{
CallbackCommand: cbapi.CallbackAfterRevokeMsgCommand, CallbackCommand: cbapi.CallbackAfterRevokeMsgCommand,
ConversationID: req.ConversationID, ConversationID: req.ConversationID,
Seq: req.Seq, Seq: req.Seq,
UserID: req.UserID, UserID: req.UserID,
} }
resp := &cbapi.CallbackAfterRevokeMsgResp{} m.webhookClient.AsyncPost(ctx, callbackReq.GetCallbackCommand(), callbackReq, &cbapi.CallbackAfterRevokeMsgResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, callbackReq, resp, callback.AfterRevokeMsg); err != nil {
return err
}
return nil
} }

View File

@ -126,8 +126,6 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil { if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil {
return nil, err return nil, err
} }
if err = CallbackAfterRevokeMsg(ctx, &m.config.WebhooksConfig, req); err != nil { m.webhookAfterRevokeMsg(ctx, &m.config.WebhooksConfig.AfterRevokeMsg, req)
return nil, err
}
return &msg.RevokeMsgResp{}, nil return &msg.RevokeMsgResp{}, nil
} }

View File

@ -59,10 +59,11 @@ func (m *msgServer) sendMsgSuperGroupChat(ctx context.Context, req *pbmsg.SendMs
prommetrics.GroupChatMsgProcessFailedCounter.Inc() prommetrics.GroupChatMsgProcessFailedCounter.Inc()
return nil, err return nil, err
} }
if err = callbackBeforeSendGroupMsg(ctx, &m.config.WebhooksConfig, req); err != nil {
if err = m.webhookBeforeSendGroupMsg(ctx, &m.config.WebhooksConfig.BeforeSendGroupMsg, req); err != nil {
return nil, err return nil, err
} }
if err := callbackMsgModify(ctx, &m.config.WebhooksConfig, req); err != nil { if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err return nil, err
} }
err = m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData) err = m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData)
@ -72,9 +73,8 @@ func (m *msgServer) sendMsgSuperGroupChat(ctx context.Context, req *pbmsg.SendMs
if req.MsgData.ContentType == constant.AtText { if req.MsgData.ContentType == constant.AtText {
go m.setConversationAtInfo(ctx, req.MsgData) go m.setConversationAtInfo(ctx, req.MsgData)
} }
if err = callbackAfterSendGroupMsg(ctx, &m.config.WebhooksConfig, req); err != nil {
log.ZWarn(ctx, "CallbackAfterSendGroupMsg", err) m.webhookAfterSendGroupMsg(ctx, &m.config.WebhooksConfig.AfterSendGroupMsg, req)
}
prommetrics.GroupChatMsgProcessSuccessCounter.Inc() prommetrics.GroupChatMsgProcessSuccessCounter.Inc()
resp = &pbmsg.SendMsgResp{} resp = &pbmsg.SendMsgResp{}
resp.SendTime = req.MsgData.SendTime resp.SendTime = req.MsgData.SendTime
@ -157,21 +157,18 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
prommetrics.SingleChatMsgProcessFailedCounter.Inc() prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, nil return nil, nil
} else { } else {
if err = callbackBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig, req); err != nil { if err = m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, req); err != nil {
return nil, err
}
if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err return nil, err
} }
if err := callbackMsgModify(ctx, &m.config.WebhooksConfig, req); err != nil {
return nil, err
}
if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil { if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
prommetrics.SingleChatMsgProcessFailedCounter.Inc() prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, err return nil, err
} }
err = callbackAfterSendSingleMsg(ctx, &m.config.WebhooksConfig, req) m.webhookAfterSendSingleMsg(ctx, &m.config.WebhooksConfig.AfterSendSingleMsg, req)
if err != nil {
log.ZWarn(ctx, "CallbackAfterSendSingleMsg", err, "req", req)
}
prommetrics.SingleChatMsgProcessSuccessCounter.Inc() prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
return &pbmsg.SendMsgResp{ return &pbmsg.SendMsgResp{
ServerMsgID: req.MsgData.ServerMsgID, ServerMsgID: req.MsgData.ServerMsgID,

View File

@ -17,6 +17,8 @@ package msg
import ( import (
"context" "context"
"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/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
@ -32,6 +34,11 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type ( type (
// MessageInterceptorChain defines a chain of message interceptor functions. // MessageInterceptorChain defines a chain of message interceptor functions.
MessageInterceptorChain []MessageInterceptorFunc MessageInterceptorChain []MessageInterceptorFunc
@ -48,6 +55,7 @@ type (
Handlers MessageInterceptorChain // Chain of handlers for processing messages. Handlers MessageInterceptorChain // Chain of handlers for processing messages.
notificationSender *rpcclient.NotificationSender // RPC client for sending notifications. notificationSender *rpcclient.NotificationSender // RPC client for sending notifications.
config *Config // Global configuration settings. config *Config // Global configuration settings.
webhookClient *webhook.Client
} }
Config struct { Config struct {
@ -101,6 +109,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, &config.LocalCacheConfig, rdb), ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, &config.LocalCacheConfig, rdb),
FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, &config.LocalCacheConfig, rdb), FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, &config.LocalCacheConfig, rdb),
config: config, config: config,
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)),
} }
s.notificationSender = rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithLocalSendMsg(s.SendMsg)) s.notificationSender = rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithLocalSendMsg(s.SendMsg))

View File

@ -20,14 +20,10 @@ import (
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"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/http"
pbuser "github.com/openimsdk/protocol/user" pbuser "github.com/openimsdk/protocol/user"
) )
func CallbackBeforeUpdateUserInfo(ctx context.Context, callback *config.Webhooks, req *pbuser.UpdateUserInfoReq) error { func (s *userServer) webhookBeforeUpdateUserInfo(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoReq) error {
if !callback.BeforeUpdateUserInfo.Enable {
return nil
}
cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{ cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand, CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand,
UserID: req.UserInfo.UserID, UserID: req.UserInfo.UserID,
@ -35,34 +31,27 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, callback *config.Webhooks
Nickname: &req.UserInfo.Nickname, Nickname: &req.UserInfo.Nickname,
} }
resp := &cbapi.CallbackBeforeUpdateUserInfoResp{} resp := &cbapi.CallbackBeforeUpdateUserInfoResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeUpdateUserInfo); err != nil { if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
datautil.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL) datautil.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL)
datautil.NotNilReplace(&req.UserInfo.Ex, resp.Ex) datautil.NotNilReplace(&req.UserInfo.Ex, resp.Ex)
datautil.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) datautil.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
return nil return nil
} }
func CallbackAfterUpdateUserInfo(ctx context.Context, callback *config.Webhooks, req *pbuser.UpdateUserInfoReq) error {
if !callback.AfterUpdateUserInfo.Enable { func (s *userServer) webhookAfterUpdateUserInfo(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoReq) {
return nil
}
cbReq := &cbapi.CallbackAfterUpdateUserInfoReq{ cbReq := &cbapi.CallbackAfterUpdateUserInfoReq{
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoCommand, CallbackCommand: cbapi.CallbackAfterUpdateUserInfoCommand,
UserID: req.UserInfo.UserID, UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL, FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname, Nickname: req.UserInfo.Nickname,
} }
resp := &cbapi.CallbackAfterUpdateUserInfoResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterUpdateUserInfo); err != nil {
return err
}
return nil
} }
func CallbackBeforeUpdateUserInfoEx(ctx context.Context, callback *config.Webhooks, req *pbuser.UpdateUserInfoExReq) error {
if !callback.BeforeUpdateUserInfoEx.Enable { func (s *userServer) webhookBeforeUpdateUserInfoEx(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoExReq) error {
return nil
}
cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{ cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand, CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
UserID: req.UserInfo.UserID, UserID: req.UserInfo.UserID,
@ -70,35 +59,27 @@ func CallbackBeforeUpdateUserInfoEx(ctx context.Context, callback *config.Webhoo
Nickname: req.UserInfo.Nickname, Nickname: req.UserInfo.Nickname,
} }
resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{} resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeUpdateUserInfoEx); err != nil { if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
datautil.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL) datautil.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
datautil.NotNilReplace(req.UserInfo.Ex, resp.Ex) datautil.NotNilReplace(req.UserInfo.Ex, resp.Ex)
datautil.NotNilReplace(req.UserInfo.Nickname, resp.Nickname) datautil.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
return nil return nil
} }
func CallbackAfterUpdateUserInfoEx(ctx context.Context, callback *config.Webhooks, req *pbuser.UpdateUserInfoExReq) error {
if !callback.AfterUpdateUserInfoEx.Enable { func (s *userServer) webhookAfterUpdateUserInfoEx(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoExReq) {
return nil
}
cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{ cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand, CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand,
UserID: req.UserInfo.UserID, UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL, FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname, Nickname: req.UserInfo.Nickname,
} }
resp := &cbapi.CallbackAfterUpdateUserInfoExResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoExResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterUpdateUserInfoEx); err != nil {
return err
}
return nil
} }
func CallbackBeforeUserRegister(ctx context.Context, callback *config.Webhooks, req *pbuser.UserRegisterReq) error { func (s *userServer) webhookBeforeUserRegister(ctx context.Context, before *config.BeforeConfig, req *pbuser.UserRegisterReq) error {
if !callback.BeforeUserRegister.Enable {
return nil
}
cbReq := &cbapi.CallbackBeforeUserRegisterReq{ cbReq := &cbapi.CallbackBeforeUserRegisterReq{
CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand, CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand,
Secret: req.Secret, Secret: req.Secret,
@ -106,28 +87,23 @@ func CallbackBeforeUserRegister(ctx context.Context, callback *config.Webhooks,
} }
resp := &cbapi.CallbackBeforeUserRegisterResp{} resp := &cbapi.CallbackBeforeUserRegisterResp{}
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.BeforeUserRegister); err != nil {
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
return err return err
} }
if len(resp.Users) != 0 { if len(resp.Users) != 0 {
req.Users = resp.Users req.Users = resp.Users
} }
return nil return nil
} }
func CallbackAfterUserRegister(ctx context.Context, callback *config.Webhooks, req *pbuser.UserRegisterReq) error { func (s *userServer) webhookAfterUserRegister(ctx context.Context, after *config.AfterConfig, req *pbuser.UserRegisterReq) {
if !callback.AfterUserRegister.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterUserRegisterReq{ cbReq := &cbapi.CallbackAfterUserRegisterReq{
CallbackCommand: cbapi.CallbackAfterUserRegisterCommand, CallbackCommand: cbapi.CallbackAfterUserRegisterCommand,
Secret: req.Secret, Secret: req.Secret,
Users: req.Users, Users: req.Users,
} }
resp := &cbapi.CallbackAfterUserRegisterResp{} s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUserRegisterResp{}, after)
if err := http.CallBackPostReturn(ctx, callback.URL, cbReq, resp, callback.AfterUserRegister); err != nil {
return err
}
return nil
} }

View File

@ -17,6 +17,8 @@ package user
import ( import (
"context" "context"
"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/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"math/rand" "math/rand"
"strings" "strings"
@ -44,6 +46,11 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const (
webhookWorkerCount = 2
webhookBufferSize = 100
)
type userServer struct { type userServer struct {
db controller.UserDatabase db controller.UserDatabase
friendNotificationSender *notification.FriendNotificationSender friendNotificationSender *notification.FriendNotificationSender
@ -52,6 +59,7 @@ type userServer struct {
groupRpcClient *rpcclient.GroupRpcClient groupRpcClient *rpcclient.GroupRpcClient
RegisterCenter registry.SvcDiscoveryRegistry RegisterCenter registry.SvcDiscoveryRegistry
config *Config config *Config
webhookClient *webhook.Client
} }
type Config struct { type Config struct {
@ -99,6 +107,7 @@ func Start(ctx context.Context, config *Config, client registry.SvcDiscoveryRegi
friendNotificationSender: notification.NewFriendNotificationSender(&config.NotificationConfig, &msgRpcClient, notification.WithDBFunc(database.FindWithError)), friendNotificationSender: notification.NewFriendNotificationSender(&config.NotificationConfig, &msgRpcClient, notification.WithDBFunc(database.FindWithError)),
userNotificationSender: NewUserNotificationSender(config, &msgRpcClient, WithUserFunc(database.FindWithError)), userNotificationSender: NewUserNotificationSender(config, &msgRpcClient, WithUserFunc(database.FindWithError)),
config: config, config: config,
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL, memAsyncQueue.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)),
} }
pbuser.RegisterUserServer(server, u) pbuser.RegisterUserServer(server, u)
return u.db.InitOnce(context.Background(), users) return u.db.InitOnce(context.Background(), users)
@ -114,15 +123,21 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
return resp, nil return resp, nil
} }
// deprecated:
//UpdateUserInfo
func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) { func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) {
resp = &pbuser.UpdateUserInfoResp{} resp = &pbuser.UpdateUserInfoResp{}
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID) err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := CallbackBeforeUpdateUserInfo(ctx, &s.config.WebhooksConfig, req); err != nil {
if err := s.webhookBeforeUpdateUserInfo(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfo, req); err != nil {
return nil, err return nil, err
} }
data := convert.UserPb2DBMap(req.UserInfo) data := convert.UserPb2DBMap(req.UserInfo)
if err := s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil { if err := s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
return nil, err return nil, err
@ -140,9 +155,7 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
for _, friendID := range friends { for _, friendID := range friends {
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID) s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
} }
if err = CallbackAfterUpdateUserInfo(ctx, &s.config.WebhooksConfig, req); err != nil { s.webhookAfterUpdateUserInfo(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfo, req)
return nil, err
}
if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
return nil, err return nil, err
} }
@ -154,8 +167,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = s.webhookBeforeUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfoEx, req); err != nil {
if err = CallbackBeforeUpdateUserInfoEx(ctx, &s.config.WebhooksConfig, req); err != nil {
return nil, err return nil, err
} }
data := convert.UserPb2DBMapEx(req.UserInfo) data := convert.UserPb2DBMapEx(req.UserInfo)
@ -175,9 +187,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
for _, friendID := range friends { for _, friendID := range friends {
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID) s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
} }
if err := CallbackAfterUpdateUserInfoEx(ctx, &s.config.WebhooksConfig, req); err != nil { s.webhookAfterUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfoEx, req)
return nil, err
}
if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
return nil, err return nil, err
} }
@ -273,7 +283,7 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR
if exist { if exist {
return nil, servererrs.ErrRegisteredAlready.WrapMsg("userID registered already") return nil, servererrs.ErrRegisteredAlready.WrapMsg("userID registered already")
} }
if err := CallbackBeforeUserRegister(ctx, &s.config.WebhooksConfig, req); err != nil { if err := s.webhookBeforeUserRegister(ctx, &s.config.WebhooksConfig.BeforeUserRegister, req); err != nil {
return nil, err return nil, err
} }
now := time.Now() now := time.Now()
@ -293,9 +303,7 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR
return nil, err return nil, err
} }
if err := CallbackAfterUserRegister(ctx, &s.config.WebhooksConfig, req); err != nil { s.webhookAfterUserRegister(ctx, &s.config.WebhooksConfig.AfterUserRegister, req)
return nil, err
}
return resp, nil return resp, nil
} }

View File

@ -64,7 +64,7 @@ type CommonCallbackResp struct {
} }
func (c CommonCallbackResp) Parse() error { func (c CommonCallbackResp) Parse() error {
if c.ActionCode != servererrs.NoError || c.NextCode == Next { if c.ActionCode == servererrs.NoError && c.NextCode == Next {
return errs.NewCodeError(int(c.ErrCode), c.ErrMsg).WithDetail(c.ErrDlt) return errs.NewCodeError(int(c.ErrCode), c.ErrMsg).WithDetail(c.ErrDlt)
} }
return nil return nil

View File

@ -14,47 +14,44 @@
package callbackstruct package callbackstruct
const CallbackBeforeInviteJoinGroupCommand = "callbackBeforeInviteJoinGroupCommand"
const CallbackAfterJoinGroupCommand = "callbackAfterJoinGroupCommand"
const CallbackAfterSetGroupInfoCommand = "callbackAfterSetGroupInfoCommand"
const CallbackBeforeSetGroupInfoCommand = "callbackBeforeSetGroupInfoCommand"
const CallbackAfterRevokeMsgCommand = "callbackBeforeAfterMsgCommand"
const CallbackBeforeAddBlackCommand = "callbackBeforeAddBlackCommand"
const CallbackAfterAddFriendCommand = "callbackAfterAddFriendCommand"
const CallbackBeforeAddFriendAgreeCommand = "callbackBeforeAddFriendAgreeCommand"
const CallbackAfterDeleteFriendCommand = "callbackAfterDeleteFriendCommand"
const CallbackBeforeImportFriendsCommand = "callbackBeforeImportFriendsCommand"
const CallbackAfterImportFriendsCommand = "callbackAfterImportFriendsCommand"
const CallbackAfterRemoveBlackCommand = "callbackAfterRemoveBlackCommand"
const ( const (
CallbackQuitGroupCommand = "callbackQuitGroupCommand" CallbackBeforeInviteJoinGroupCommand = "callbackBeforeInviteJoinGroupCommand"
CallbackKillGroupCommand = "callbackKillGroupCommand" CallbackAfterJoinGroupCommand = "callbackAfterJoinGroupCommand"
CallbackDisMissGroupCommand = "callbackDisMissGroupCommand" CallbackAfterSetGroupInfoCommand = "callbackAfterSetGroupInfoCommand"
CallbackBeforeSetGroupInfoCommand = "callbackBeforeSetGroupInfoCommand"
CallbackAfterRevokeMsgCommand = "callbackBeforeAfterMsgCommand"
CallbackBeforeAddBlackCommand = "callbackBeforeAddBlackCommand"
CallbackAfterAddFriendCommand = "callbackAfterAddFriendCommand"
CallbackBeforeAddFriendAgreeCommand = "callbackBeforeAddFriendAgreeCommand"
CallbackAfterDeleteFriendCommand = "callbackAfterDeleteFriendCommand"
CallbackBeforeImportFriendsCommand = "callbackBeforeImportFriendsCommand"
CallbackAfterImportFriendsCommand = "callbackAfterImportFriendsCommand"
CallbackAfterRemoveBlackCommand = "callbackAfterRemoveBlackCommand"
CallbackAfterQuitGroupCommand = "callbackAfterQuitGroupCommand"
CallbackAfterKickGroupCommand = "callbackAfterKickGroupCommand"
CallbackAfterDisMissGroupCommand = "callbackAfterDisMissGroupCommand"
CallbackBeforeJoinGroupCommand = "callbackBeforeJoinGroupCommand" CallbackBeforeJoinGroupCommand = "callbackBeforeJoinGroupCommand"
CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand" CallbackAfterGroupMsgReadCommand = "callbackAfterGroupMsgReadCommand"
CallbackMsgModifyCommand = "callbackMsgModifyCommand" CallbackBeforeMsgModifyCommand = "callbackBeforeMsgModifyCommand"
CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand" CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand"
CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand" CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand"
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand" CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand" CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand" CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
CallbackAfterTransferGroupOwner = "callbackAfterTransferGroupOwner" CallbackAfterTransferGroupOwnerCommand = "callbackAfterTransferGroupOwnerCommand"
CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark" CallbackBeforeSetFriendRemarkCommand = "callbackBeforeSetFriendRemarkCommand"
CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark" CallbackAfterSetFriendRemarkCommand = "callbackAfterSetFriendRemarkCommand"
CallbackSingleMsgRead = "callbackSingleMsgRead" CallbackAfterSingleMsgReadCommand = "callbackAfterSingleMsgReadCommand"
CallbackBeforeSendSingleMsgCommand = "callbackBeforeSendSingleMsgCommand" CallbackBeforeSendSingleMsgCommand = "callbackBeforeSendSingleMsgCommand"
CallbackAfterSendSingleMsgCommand = "callbackAfterSendSingleMsgCommand" CallbackAfterSendSingleMsgCommand = "callbackAfterSendSingleMsgCommand"
CallbackBeforeSendGroupMsgCommand = "callbackBeforeSendGroupMsgCommand" CallbackBeforeSendGroupMsgCommand = "callbackBeforeSendGroupMsgCommand"
CallbackAfterSendGroupMsgCommand = "callbackAfterSendGroupMsgCommand" CallbackAfterSendGroupMsgCommand = "callbackAfterSendGroupMsgCommand"
CallbackUserOnlineCommand = "callbackUserOnlineCommand" CallbackAfterUserOnlineCommand = "callbackAfterUserOnlineCommand"
CallbackUserOfflineCommand = "callbackUserOfflineCommand" CallbackAfterUserOfflineCommand = "callbackAfterUserOfflineCommand"
CallbackUserKickOffCommand = "callbackUserKickOffCommand" CallbackAfterUserKickOffCommand = "callbackAfterUserKickOffCommand"
CallbackOfflinePushCommand = "callbackOfflinePushCommand" CallbackBeforeOfflinePushCommand = "callbackBeforeOfflinePushCommand"
CallbackOnlinePushCommand = "callbackOnlinePushCommand" CallbackBeforeOnlinePushCommand = "callbackBeforeOnlinePushCommand"
CallbackSuperGroupOnlinePushCommand = "callbackSuperGroupOnlinePushCommand" CallbackBeforeGroupOnlinePushCommand = "callbackBeforeGroupOnlinePushCommand"
CallbackBeforeAddFriendCommand = "callbackBeforeAddFriendCommand" CallbackBeforeAddFriendCommand = "callbackBeforeAddFriendCommand"
CallbackBeforeUpdateUserInfoCommand = "callbackBeforeUpdateUserInfoCommand" CallbackBeforeUpdateUserInfoCommand = "callbackBeforeUpdateUserInfoCommand"
CallbackBeforeCreateGroupCommand = "callbackBeforeCreateGroupCommand" CallbackBeforeCreateGroupCommand = "callbackBeforeCreateGroupCommand"

View File

@ -339,12 +339,17 @@ type Redis struct {
MaxRetry int `mapstructure:"MaxRetry"` MaxRetry int `mapstructure:"MaxRetry"`
} }
type WebhookConfig struct { type BeforeConfig struct {
Enable bool `mapstructure:"enable"` Enable bool `mapstructure:"enable"`
Timeout int `mapstructure:"timeout"` Timeout int `mapstructure:"timeout"`
FailedContinue bool `mapstructure:"failedContinue"` FailedContinue bool `mapstructure:"failedContinue"`
} }
type AfterConfig struct {
Enable bool `mapstructure:"enable"`
Timeout int `mapstructure:"timeout"`
}
type Share struct { type Share struct {
Secret string `mapstructure:"secret"` Secret string `mapstructure:"secret"`
Env string `mapstructure:"env"` Env string `mapstructure:"env"`
@ -377,53 +382,54 @@ func (r *RpcRegisterName) GetServiceNames() []string {
} }
} }
// FullConfig stores all configurations for before and after events
type Webhooks struct { type Webhooks struct {
URL string `mapstructure:"url"` URL string `mapstructure:"url"`
BeforeSendSingleMsg WebhookConfig `mapstructure:"beforeSendSingleMsg"` BeforeSendSingleMsg BeforeConfig `mapstructure:"beforeSendSingleMsg"`
BeforeUpdateUserInfoEx WebhookConfig `mapstructure:"beforeUpdateUserInfoEx"` BeforeUpdateUserInfoEx BeforeConfig `mapstructure:"beforeUpdateUserInfoEx"`
AfterUpdateUserInfoEx WebhookConfig `mapstructure:"afterUpdateUserInfoEx"` AfterUpdateUserInfoEx AfterConfig `mapstructure:"afterUpdateUserInfoEx"`
AfterSendSingleMsg WebhookConfig `mapstructure:"afterSendSingleMsg"` AfterSendSingleMsg AfterConfig `mapstructure:"afterSendSingleMsg"`
BeforeSendGroupMsg WebhookConfig `mapstructure:"beforeSendGroupMsg"` BeforeSendGroupMsg BeforeConfig `mapstructure:"beforeSendGroupMsg"`
AfterSendGroupMsg WebhookConfig `mapstructure:"afterSendGroupMsg"` BeforeMsgModify BeforeConfig `mapstructure:"beforeMsgModify"`
BeforeMsgModify WebhookConfig `mapstructure:"beforeMsgModify"` AfterSendGroupMsg AfterConfig `mapstructure:"afterSendGroupMsg"`
AfterUserOnline WebhookConfig `mapstructure:"afterUserOnline"` AfterUserOnline AfterConfig `mapstructure:"afterUserOnline"`
AfterUserOffline WebhookConfig `mapstructure:"afterUserOffline"` AfterUserOffline AfterConfig `mapstructure:"afterUserOffline"`
AfterUserKickOff WebhookConfig `mapstructure:"afterUserKickOff"` AfterUserKickOff AfterConfig `mapstructure:"afterUserKickOff"`
BeforeOfflinePush WebhookConfig `mapstructure:"beforeOfflinePush"` BeforeOfflinePush BeforeConfig `mapstructure:"beforeOfflinePush"`
BeforeOnlinePush WebhookConfig `mapstructure:"beforeOnlinePush"` BeforeOnlinePush BeforeConfig `mapstructure:"beforeOnlinePush"`
BeforeGroupOnlinePush WebhookConfig `mapstructure:"beforeGroupOnlinePush"` BeforeGroupOnlinePush BeforeConfig `mapstructure:"beforeGroupOnlinePush"`
BeforeAddFriend WebhookConfig `mapstructure:"beforeAddFriend"` BeforeAddFriend BeforeConfig `mapstructure:"beforeAddFriend"`
BeforeUpdateUserInfo WebhookConfig `mapstructure:"beforeUpdateUserInfo"` BeforeUpdateUserInfo BeforeConfig `mapstructure:"beforeUpdateUserInfo"`
AfterUpdateUserInfo WebhookConfig `mapstructure:"afterUpdateUserInfo"` AfterUpdateUserInfo AfterConfig `mapstructure:"afterUpdateUserInfo"`
BeforeCreateGroup WebhookConfig `mapstructure:"beforeCreateGroup"` BeforeCreateGroup BeforeConfig `mapstructure:"beforeCreateGroup"`
AfterCreateGroup WebhookConfig `mapstructure:"afterCreateGroup"` AfterCreateGroup AfterConfig `mapstructure:"afterCreateGroup"`
BeforeMemberJoinGroup WebhookConfig `mapstructure:"beforeMemberJoinGroup"` BeforeMemberJoinGroup BeforeConfig `mapstructure:"beforeMemberJoinGroup"`
BeforeSetGroupMemberInfo WebhookConfig `mapstructure:"beforeSetGroupMemberInfo"` BeforeSetGroupMemberInfo BeforeConfig `mapstructure:"beforeSetGroupMemberInfo"`
AfterSetGroupMemberInfo WebhookConfig `mapstructure:"afterSetGroupMemberInfo"` AfterSetGroupMemberInfo AfterConfig `mapstructure:"afterSetGroupMemberInfo"`
AfterQuitGroup WebhookConfig `mapstructure:"afterQuitGroup"` AfterQuitGroup AfterConfig `mapstructure:"afterQuitGroup"`
AfterKickGroupMember WebhookConfig `mapstructure:"afterKickGroupMember"` AfterKickGroupMember AfterConfig `mapstructure:"afterKickGroupMember"`
AfterDismissGroup WebhookConfig `mapstructure:"afterDismissGroup"` AfterDismissGroup AfterConfig `mapstructure:"afterDismissGroup"`
BeforeApplyJoinGroup WebhookConfig `mapstructure:"beforeApplyJoinGroup"` BeforeApplyJoinGroup BeforeConfig `mapstructure:"beforeApplyJoinGroup"`
AfterGroupMsgRead WebhookConfig `mapstructure:"afterGroupMsgRead"` AfterGroupMsgRead AfterConfig `mapstructure:"afterGroupMsgRead"`
AfterSingleMsgRead WebhookConfig `mapstructure:"afterSingleMsgRead"` AfterSingleMsgRead AfterConfig `mapstructure:"afterSingleMsgRead"`
BeforeUserRegister WebhookConfig `mapstructure:"beforeUserRegister"` BeforeUserRegister BeforeConfig `mapstructure:"beforeUserRegister"`
AfterUserRegister WebhookConfig `mapstructure:"afterUserRegister"` AfterUserRegister AfterConfig `mapstructure:"afterUserRegister"`
AfterTransferGroupOwner WebhookConfig `mapstructure:"afterTransferGroupOwner"` AfterTransferGroupOwner AfterConfig `mapstructure:"afterTransferGroupOwner"`
BeforeSetFriendRemark WebhookConfig `mapstructure:"beforeSetFriendRemark"` BeforeSetFriendRemark BeforeConfig `mapstructure:"beforeSetFriendRemark"`
AfterSetFriendRemark WebhookConfig `mapstructure:"afterSetFriendRemark"` AfterSetFriendRemark AfterConfig `mapstructure:"afterSetFriendRemark"`
AfterGroupMsgRevoke WebhookConfig `mapstructure:"afterGroupMsgRevoke"` AfterGroupMsgRevoke AfterConfig `mapstructure:"afterGroupMsgRevoke"`
AfterJoinGroup WebhookConfig `mapstructure:"afterJoinGroup"` AfterJoinGroup AfterConfig `mapstructure:"afterJoinGroup"`
BeforeInviteUserToGroup WebhookConfig `mapstructure:"beforeInviteUserToGroup"` BeforeInviteUserToGroup BeforeConfig `mapstructure:"beforeInviteUserToGroup"`
AfterSetGroupInfo WebhookConfig `mapstructure:"afterSetGroupInfo"` AfterSetGroupInfo AfterConfig `mapstructure:"afterSetGroupInfo"`
BeforeSetGroupInfo WebhookConfig `mapstructure:"beforeSetGroupInfo"` BeforeSetGroupInfo BeforeConfig `mapstructure:"beforeSetGroupInfo"`
AfterRevokeMsg WebhookConfig `mapstructure:"afterRevokeMsg"` AfterRevokeMsg AfterConfig `mapstructure:"afterRevokeMsg"`
BeforeAddBlack WebhookConfig `mapstructure:"beforeAddBlack"` BeforeAddBlack BeforeConfig `mapstructure:"beforeAddBlack"`
AfterAddFriend WebhookConfig `mapstructure:"afterAddFriend"` AfterAddFriend AfterConfig `mapstructure:"afterAddFriend"`
BeforeAddFriendAgree WebhookConfig `mapstructure:"beforeAddFriendAgree"` BeforeAddFriendAgree BeforeConfig `mapstructure:"beforeAddFriendAgree"`
AfterDeleteFriend WebhookConfig `mapstructure:"afterDeleteFriend"` AfterDeleteFriend AfterConfig `mapstructure:"afterDeleteFriend"`
BeforeImportFriends WebhookConfig `mapstructure:"beforeImportFriends"` BeforeImportFriends BeforeConfig `mapstructure:"beforeImportFriends"`
AfterImportFriends WebhookConfig `mapstructure:"afterImportFriends"` AfterImportFriends AfterConfig `mapstructure:"afterImportFriends"`
AfterRemoveBlack WebhookConfig `mapstructure:"afterRemoveBlack"` AfterRemoveBlack AfterConfig `mapstructure:"afterRemoveBlack"`
} }
type ZooKeeper struct { type ZooKeeper struct {

View File

@ -1,68 +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.
package http
import (
"context"
"encoding/json"
"net/http"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/httputil"
)
var (
// Define http client.
client = httputil.NewHTTPClient(httputil.NewClientConfig())
)
func init() {
// reset http default transport
http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // default: 2
}
func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.WebhookConfig) error {
url = url + "/" + command
log.ZInfo(ctx, "callback", "url", url, "input", input, "config", callbackConfig)
b, err := client.Post(ctx, url, nil, input, callbackConfig.Timeout)
if err != nil {
if callbackConfig.FailedContinue {
log.ZInfo(ctx, "callback failed but continue", err, "url", url)
return nil
}
log.ZWarn(ctx, "callback network failed", err, "url", url, "input", input)
return servererrs.ErrNetwork.WrapMsg(err.Error())
}
if err = json.Unmarshal(b, output); err != nil {
if callbackConfig.FailedContinue {
log.ZWarn(ctx, "callback failed but continue", err, "url", url)
return nil
}
log.ZWarn(ctx, "callback json unmarshal failed", err, "url", url, "input", input, "response", string(b))
return servererrs.ErrData.WithDetail(err.Error() + "response format error")
}
if err := output.Parse(); err != nil {
log.ZWarn(ctx, "callback parse failed", err, "url", url, "input", input, "response", string(b))
}
log.ZInfo(ctx, "callback success", "url", url, "input", input, "response", string(b))
return nil
}
func CallBackPostReturn(ctx context.Context, url string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, callbackConfig config.WebhookConfig) error {
return callBackPostReturn(ctx, url, req.GetCallbackCommand(), req, resp, callbackConfig)
}

View File

@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package http // import "github.com/openimsdk/open-im-server/v3/pkg/common/http" package webhook // import "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"

View File

@ -0,0 +1,74 @@
// 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.
package webhook
import (
"context"
"encoding/json"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/util/memAsyncQueue"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/httputil"
"net/http"
)
type Client struct {
client *httputil.HTTPClient
url string
queue *memAsyncQueue.MemoryQueue
}
func NewWebhookClient(url string, queue *memAsyncQueue.MemoryQueue) *Client {
http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // Enhance the default number of max connections per host
return &Client{
client: httputil.NewHTTPClient(httputil.NewClientConfig()),
url: url,
queue: queue,
}
}
func (c *Client) SyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, before *config.BeforeConfig) error {
if before.Enable {
return c.post(ctx, command, req, resp, before.Timeout)
}
return nil
}
func (c *Client) AsyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, after *config.AfterConfig) {
if after.Enable {
c.queue.Push(func() { c.post(ctx, command, req, resp, after.Timeout) })
}
}
func (c *Client) post(ctx context.Context, command string, input interface{}, output callbackstruct.CallbackResp, timeout int) error {
fullURL := c.url + "/" + command
log.ZInfo(ctx, "webhook", "url", fullURL, "input", input, "config", timeout)
operationID, _ := ctx.Value(constant.OperationID).(string)
b, err := c.client.Post(ctx, fullURL, map[string]string{constant.OperationID: operationID}, input, timeout)
if err != nil {
return servererrs.ErrNetwork.WrapMsg(err.Error(), "post url", fullURL)
}
if err = json.Unmarshal(b, output); err != nil {
return servererrs.ErrData.WithDetail(err.Error() + " response format error")
}
if err := output.Parse(); err != nil {
return err
}
log.ZInfo(ctx, "webhook success", "url", fullURL, "input", input, "response", string(b))
return nil
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package http package webhook
import ( import (
"context" "context"

View File

@ -0,0 +1,74 @@
package memAsyncQueue
import (
"errors"
"fmt"
"sync"
"time"
)
// AsyncQueue is the interface responsible for asynchronous processing of functions.
type AsyncQueue interface {
Initialize(processFunc func(), workerCount int, bufferSize int)
Push(task func()) error
}
// MemoryQueue is an implementation of the AsyncQueue interface using a channel to process functions.
type MemoryQueue struct {
taskChan chan func()
wg sync.WaitGroup
isStopped bool
stopMutex sync.Mutex // Mutex to protect access to isStopped
}
func NewMemoryQueue(workerCount int, bufferSize int) *MemoryQueue {
mq := &MemoryQueue{} // Create a new instance of MemoryQueue
mq.Initialize(workerCount, bufferSize) // Initialize it with specified parameters
return mq
}
// Initialize sets up the worker nodes and the buffer size of the channel,
// starting internal goroutines to handle tasks from the channel.
func (mq *MemoryQueue) Initialize(workerCount int, bufferSize int) {
mq.taskChan = make(chan func(), bufferSize) // Initialize the channel with the provided buffer size.
mq.isStopped = false
// Start multiple goroutines based on the specified workerCount.
for i := 0; i < workerCount; i++ {
mq.wg.Add(1)
go func(workerID int) {
defer mq.wg.Done()
for task := range mq.taskChan {
fmt.Printf("Worker %d: Executing task\n", workerID)
task() // Execute the function
}
}(i)
}
}
// Push submits a function to the queue.
// Returns an error if the queue is stopped or if the queue is full.
func (mq *MemoryQueue) Push(task func()) error {
mq.stopMutex.Lock()
if mq.isStopped {
mq.stopMutex.Unlock()
return errors.New("push failed: queue is stopped")
}
mq.stopMutex.Unlock()
select {
case mq.taskChan <- task:
return nil
case <-time.After(time.Millisecond * 100): // Timeout to prevent deadlock/blocking
return errors.New("push failed: queue is full")
}
}
// Stop is used to terminate the internal goroutines and close the channel.
func (mq *MemoryQueue) Stop() {
mq.stopMutex.Lock()
mq.isStopped = true
close(mq.taskChan)
mq.stopMutex.Unlock()
mq.wg.Wait()
}

View File

@ -0,0 +1,91 @@
package memAsyncQueue
import (
"testing"
"time"
)
// TestPushSuccess tests the successful pushing of data into the queue.
func TestPushSuccess(t *testing.T) {
queue := &MemoryQueue{}
queue.Initialize(func(data any) {}, 1, 5) // Small buffer size for test
// Try to push data that should succeed
err := queue.Push("test data")
if err != nil {
t.Errorf("Push should succeed, but got error: %v", err)
}
}
// TestPushFailWhenFull tests that pushing to a full queue results in an error.
func TestPushFailWhenFull(t *testing.T) {
queue := &MemoryQueue{}
queue.Initialize(func(data any) {
time.Sleep(100 * time.Millisecond) // Simulate work to delay processing
}, 1, 1) // Very small buffer to fill quickly
queue.Push("data 1") // Fill the buffer
err := queue.Push("data 2") // This should fail
if err == nil {
t.Error("Expected an error when pushing to full queue, but got none")
}
}
// TestPushFailWhenStopped tests that pushing to a stopped queue results in an error.
func TestPushFailWhenStopped(t *testing.T) {
queue := &MemoryQueue{}
queue.Initialize(func(data any) {}, 1, 1)
queue.Stop() // Stop the queue before pushing
err := queue.Push("test data")
if err == nil {
t.Error("Expected an error when pushing to stopped queue, but got none")
}
}
// TestQueueOperationSequence tests a sequence of operations to ensure the queue handles them correctly.
func TestQueueOperationSequence(t *testing.T) {
queue := &MemoryQueue{}
queue.Initialize(func(data any) {}, 1, 2)
// Sequence of pushes and a stop
err := queue.Push("data 1")
if err != nil {
t.Errorf("Failed to push data 1: %v", err)
}
err = queue.Push("data 2")
if err != nil {
t.Errorf("Failed to push data 2: %v", err)
}
queue.Stop() // Stop the queue
err = queue.Push("data 3") // This push should fail
if err == nil {
t.Error("Expected an error when pushing after stop, but got none")
}
}
// TestBlockingOnFull tests that the queue does not block indefinitely when full.
func TestBlockingOnFull(t *testing.T) {
queue := &MemoryQueue{}
queue.Initialize(func(data any) {
time.Sleep(1 * time.Second) // Simulate a long processing time
}, 1, 1)
queue.Push("data 1") // Fill the queue
start := time.Now()
err := queue.Push("data 2") // This should time out
duration := time.Since(start)
if err == nil {
t.Error("Expected an error due to full queue, but got none")
}
if duration >= time.Second {
t.Errorf("Push blocked for too long, duration: %v", duration)
}
}

View File

@ -35,8 +35,8 @@ DRY_RUN=${DRY_RUN:-""}
REGENERATE_DOCS=${REGENERATE_DOCS:-""} REGENERATE_DOCS=${REGENERATE_DOCS:-""}
UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream} UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream}
FORK_REMOTE=${FORK_REMOTE:-origin} FORK_REMOTE=${FORK_REMOTE:-origin}
MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')} MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/webhook[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')}
MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')} MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/webhook[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')}
if [[ -z ${GITHUB_USER:-} ]]; then if [[ -z ${GITHUB_USER:-} ]]; then
openim::log::error_exit "Please export GITHUB_USER=<your-user> (or GH organization, if that's where your fork lives)" openim::log::error_exit "Please export GITHUB_USER=<your-user> (or GH organization, if that's where your fork lives)"

View File

@ -101,7 +101,7 @@ func main() {
} }
/* func getLatestVersion(url string) (string, error) { /* func getLatestVersion(url string) (string, error) {
resp, err := http.Get(url) resp, err := webhook.Get(url)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -392,7 +392,7 @@ func (m *Manage) HttpGet(ctx context.Context, url string) (*http.Response, error
} }
if response.StatusCode != http.StatusOK { if response.StatusCode != http.StatusOK {
_ = response.Body.Close() _ = response.Body.Close()
return nil, fmt.Errorf("http get %s status %s", url, response.Status) return nil, fmt.Errorf("webhook get %s status %s", url, response.Status)
} }
return response, nil return response, nil
} }